动态规划——区间dp [石子合并]

这篇具有很好参考价值的文章主要介绍了动态规划——区间dp [石子合并]。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

动态规划——区间dp [石子合并],C++专栏,动态规划,算法

什么是动态规划

动态规划(dp)是一种通过将问题分解为子问题,并利用已解决的子问题的解来求解原问题的方法。适用于具有重叠子问题和最优子结构性质的优化问题。通过定义状态和状态转移方程,动态规划可以在避免重复计算的同时找到问题的最优解,是一种高效的求解方法,常用于解决各种问题,如最短路径、背包问题、序列比对等。


区间dp

动态规划——区间dp [石子合并],C++专栏,动态规划,算法

定义

区间dp是一种dp的应用,用于解决涉及区间的问题。
它将问题划分为若干个子区间,并通过定义状态和状态转移方程来求解每个子区间的最优解,最终得到整个区间的最优解。


应用

区间动态规划常用于解决一些涉及区间操作的问题,如最长公共子序列、最长回文子串等。在区间动态规划中,通常需要定义一个二维数组来表示子区间的状态,并通过填表的方式逐步求解子区间的最优解,最后得到整个区间的最优解。区间动态规划是一种高效的求解方法,可以有效地解决各种区间问题。



例题引入

石子合并是一道非常经典的区间DP的例题,很适合新手练习,下面我们看看这个题目:

题目描述

在一个圆形操场的四周摆放 N N N 堆石子,现要将石子有次序地合并成一堆,规定每次只能选相邻的 2 2 2 堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出一个算法,计算出将 N N N 堆石子合并成 1 1 1 堆的最小得分和最大得分。

输入格式

数据的第 1 1 1 行是正整数 N N N,表示有 N N N 堆石子。

2 2 2 行有 N N N 个整数,第 i i i 个整数 a i a_i ai 表示第 i i i 堆石子的个数。

输出格式

输出共 2 2 2 行,第 1 1 1 行为最小得分,第 2 2 2 行为最大得分。

样例

样例输入

4
4 5 9 4

样例输出

43
54

提示

1 ≤ N ≤ 100 1\leq N\leq 100 1N100 0 ≤ a i ≤ 20 0\leq a_i\leq 20 0ai20



贪心法

稍微想想就知道不可行,因为它每次只能合并相邻的两堆石子,用贪心的话会答案错误,因此我们考虑区间dp。


区间dp

利用区间动态规划来解决此类问题。

优缺点:

优点 缺点
可以求出正解 时间复杂度高
代码量相对较少 效率低
容易理解 数据量大会超时

确实,此题的时间复杂度高达 O ( 2 n 3 ) O(2n^3) O(2n3),若非这道题的数据量极小,下面的代码便会超时,那么就需要用一个特殊方法来优化一下。

AC代码:

#include <bits/stdc++.h>
using namespace std;
int n,ans1=0x7fffffff,ans2,a[300],s[300];
int mi[300][300],ma[300][300];
int main() {
	cin >>n;
	for (int i=1; i<=n; i++) {
		cin >>a[i];
		a[i+n]=a[i];
	}
	for (int i=1; i<=2*n; i++)
		s[i]=s[i-1]+a[i];
	memset(mi,0x3f,sizeof(mi));
	for (int i=1; i<=2*n; i++)
		mi[i][i]=0;
	for (int l=1; l<n; l++) {
		for (int i=1; i+l<=2*n; i++) {
			for (int k=i; k<i+l; k++) {
				mi[i][i+l]=min(mi[i][i+l],mi[i][k]+mi[k+1][i+l]+s[i+l]-s[i-1]);
				ma[i][i+l]=max(ma[i][i+l],ma[i][k]+ma[k+1][i+l]+s[i+l]-s[i-1]);
			}
		}
	}
	for (int i=1; i<n; i++)
		ans1=min(ans1,mi[i][i+n-1]);
	for (int i=1; i<n; i++)
		ans2=max(ans2,ma[i][i+n-1]);
	cout <<ans1 <<endl <<ans2;
	return 0;
}

代码详解

三层for循环

  • 第一层for循环用来分区间,我们可以选择将一个大区间分成若干个小区间,那么分的标准是什么呢?这就是第一层for循环的作用, l l l就是每次分的区间长度。
  • 第二层循环就是定义区间的左端点, k = 1 k=1 k=1,再根据 l = 2 l=2 l=2,就能确定区间为 ( 1 , 2 ) (1,2) (1,2),即 f [ 1 ] [ 2 ] f[1][2] f[1][2]
  • 然后第三层循环就是拆分了,如上图, l = 2 l=2 l=2的时候是无法拆分了,但是 l = 3 l=3 l=3的时候是可以拆成 f [ 1 ] [ 2 ] , f [ 3 ] [ 3 ] , f [ 1 ] [ 1 ] , f [ 2 ] [ 3 ] f[1][2],f[3][3],f[1][1],f[2][3] f[1][2],f[3][3],f[1][1],f[2][3],以及各个数据都有或没有拆分方法。

状态转移方程

区间拆分了,那么我们如何去算转移方程呢?我们将两个区间 ( 1 , 2 ) (1,2) (1,2) ( 3 , 4 ) (3,4) (3,4)进行合并,那么就是 ( 1 , 2 ) (1,2) (1,2)的代价加上 ( 3 , 4 ) (3,4) (3,4)的代价,再加上1到4的区间和,具体的式子如下(下面用 f f f数组代表dp所用的数组)

f[1][4] = min(f[1][4],f[1][2]+f[3][4]+s[4]-s[0]);

为什么这么计算呢,因为你要知道你在求解的过程中是通过小的区间组合成较大的区间的,而你所用到的这两个小的区间 f [ 1 ] [ 2 ] + f [ 3 ] [ 4 ] f[1][2]+f[3][4] f[1][2]+f[3][4],已经得出了最优解了,那么得出最优解需不需要花费代价呢?当然需要,所以我们需要加上那段花费,那么简单来说就是

f[1][2]+f[3][4]=s[2]-s[0]+s[4]-s[2]

因此,状态转移方程便有了:

f[i][i+l]=min(f[i][i+l],f[i][k]+f[k+1][i+l]+s[i+l]-s[i-1]);

环形的处理

我们仔细阅读题目即可发现:石子是按照环形摆放的,因此区间的头和尾是能够合并的,那么如何处理呢?
方法很简单,只需要将整一条线性的石头复制一个,让第二个的头对着第一个的尾,然后正常处理即可。
这一点在程序的很多地方可以体现出来,如:

	for (int i=1; i<=n; i++) {
		cin >>a[i];
		a[i+n]=a[i];
	}

	for (int i=1; i<=2*n; i++)

以及

	for (int i=1; i+l<=2*n; i++)

等等。文章来源地址https://www.toymoban.com/news/detail-533080.html

到了这里,关于动态规划——区间dp [石子合并]的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 算法基础复盘笔记Day11【动态规划】—— 区间DP、计数类DP、树形DP、记忆化搜索

    ❤ 作者主页:欢迎来到我的技术博客😎 ❀ 个人介绍:大家好,本人热衷于 Java后端开发 ,欢迎来交流学习哦!( ̄▽ ̄)~* 🍊 如果文章对您有帮助,记得 关注 、 点赞 、 收藏 、 评论 ⭐️⭐️⭐️ 📣 您的支持将是我创作的动力,让我们一起加油进步吧!!!🎉🎉 1. 题目

    2024年02月01日
    浏览(41)
  • AcWing算法学习笔记:动态规划(背包 + 线性dp + 区间dp + 计数dp + 状态压缩dp + 树形dp + 记忆化搜索)

    算法 复杂度 时间复杂度0(nm) 空间复杂度0(nv) 代码 算法 通过滚动数组对01背包朴素版进行空间上的优化 f[i] 与 f[i - 1]轮流交替 若体积从小到大进行遍历,当更新f[i, j]时,f[i - 1, j - vi] 已经在更新f[i, j - vi]时被更新了 因此体积需要从大到小进行遍历,当更新f[i, j]时,f[i - 1,

    2024年02月21日
    浏览(43)
  • 石子合并问题(动态规划)

    石子合并问题是一个经典的动态规划问题,应用了最优子结构和重复子问题的思想。 (1)有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动任意的2堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成 (动态规划)O(n^3) 设dp[i][j]表示将i至j之

    2024年02月06日
    浏览(46)
  • 合并石子(动态规划)

    首先理解题意,笔者看到这道题时首先想到的是贪心,仔细分析后发现题上没说石子根据一定顺序来摆放,易证局部最优解不一定是问题最优解,没有贪心性质,不能用贪心 然后需要强调的是,该题是一个圆形结构 这里为了方便理解,我们先将其简化为线性结构 先看一个简

    2024年02月01日
    浏览(35)
  • 【动态规划】之石子合并问题

    在一个操场上一排地摆放着N堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。请设计一个程序,计算出将N堆石子合并成一堆的最小得分和最大得分。 我们对n的取值逐步分析 当n=1时,没有进

    2024年04月28日
    浏览(36)
  • 租用游艇问题 石子合并问题 动态规划实验

    实验名称:                动态规划                          一、实验预习 1、实验目的 1. 理解并掌握动态规划方法的设计思想; 2. 提高应用动态规划方法解决问题和设计算法的能力; 3. 通过编程实现租用游艇问题和石子合并问题,进一步理解动态规划方

    2024年02月07日
    浏览(47)
  • C++动态规划经典案例解析之合并石子

    区间类型问题,指求一个数列中某一段区间的值,包括求和、最值等简单或复杂问题。此类问题也适用于动态规划思想。 如 前缀和 就是极简单的区间问题。如有如下数组: 现给定区间信息 [3,6] ,求区间内所有数字相加结果。即求如下图位置数字之和。 Tips: 区间至少包括

    2024年02月11日
    浏览(38)
  • 区间dp(动态规划)

    动态规划(dp)是一种通过将问题分解为子问题,并利用已解决的子问题的解来求解原问题的方法。适用于具有重叠子问题和最优子结构性质的优化问题。通过定义状态和状态转移方程,动态规划可以在避免重复计算的同时找到问题的最优解,是一种高效的求解方法,常用于

    2024年02月15日
    浏览(41)
  • 动态规划——区间DP 学习笔记

    不含四边形不等式优化。 线性动态规划的局限性在于,它只能顺推或倒退,而不能有子区间依赖的问题。 区间动态规划是线性动态规划的扩展,它将问题划分为若干个子区间,并通过定义状态和状态转移方程来求解每个子区间的最优解,最终得到整个区间的最优解。 区间动

    2024年02月08日
    浏览(42)
  • 动态规划系列 | 一文搞定区间DP

    区间 DP 可以用于解决一些涉及到区间合并或分割的问题。区间 DP 通常有以下三个特点: 合并(分割) :将两个或多个部分进行整合,或者反过来将一个区间分解成多个部分。 特征 :能将问题分解为能两两合并的形式。 求解 :对整个问题设最优解,枚举合并点,将问题分

    2024年02月02日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包