区间动态规划的含义与模板解释
区间DP,其实求的就是一个区间内的最优值.
一般这种题目,在设置状态的时候,都可以设f[i][j]为区间i-j的最优值
而f[i][j]的最优值,这有两个小区间合并而来的,为了划分这两个更小的区间,我们则需用用一个循环变量k来枚举,而一般的状态转移方程便是:
f[i][j] = max/min (f[i][j] , f[i][k] + f[k][j] + something)
我们则需要根据这个题目的实际含义进行变通即可.
而区间dp的大致模板是:
for (int len=2;len<=n;len++)
for (int i=1;i+len-1<=n;i++)
{ int j=i+len-1;
for (int k=i;k<=j;k++)
f[i][j]=max/min(f[i][j],f[i][k]+f[k][j]+something)
}
len枚举区间的长度,i和j分别是区间的起点和终点,k的作用是用来划分区间.
例1、石子合并
题目描述
在一个圆形操场的四周摆放 N 堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出一个算法,计算出将 N 堆石子合并成 1 堆的最小得分和最大得分。
输入格式
数据的第 1 行是正整数 N,表示有 N 堆石子。
第 2 行有 N 个整数,第 i 个整数 ai 表示第 i 堆石子的个数。
输出格式
输出共 2 行,第 1行为最小得分,第 2 行为最大得分。
输入输出样例
输入
4
4 5 9 4
输出
43
54
说明/提示
1≤N≤100,0≤ai≤20。
分析
一、确定算法
第一眼看见:最小得分与最大得分,便想到要用贪心或dp。贪心能用吗?不能,因为只有相邻两堆才能合并,这样就无法贪心啦!
所以,此题正解就是 区间dp
二、前置操作
当一条链来做, 最后一次合并一定是两堆石子合并,从第一个元素开始,这两堆石子有可能的是:
第1种可能性:(4),(5,9,4) 4是一堆石子,(5,9,4)合并的一堆石子 结果为:14+18+22 =54
第2种可能性:(4,5),(9,4) 结果为:9+13+21=43
第3种可能性:(4,5,9),(4) 结果为:14+18+22=54
所以,我们更加抽象一点来思考,设dp(i,j)表示第i堆石子到第j堆石子合并成一堆石子的最大得分,a(i,j)表示第i到第j的和:
所以一条链的石子合并就OK了,关键环形是怎么做。
首先,发现要在一个首尾相连的环上dp并不方便,于是可以断环为链 ,
假设输入的数是a[1],a[2],……,a[n],那么使a[n+1]=a[1],a[n+2]=a[2],……,a[2*n]=a[n] 。
你会发现n个数的环上的每一段都在2*n个数的链上了 ,于是就在链上dp等价于在环上dp 。
三、动态规划三部曲:
1、状态
设dp1[i][j]表示把从i到j的石子合并为一堆的最小得分,dp2[i][j]表示把从i到j的石子合并为一堆的最大得分(i<j)
2、转移方程
以最小值为例:
假设存在k使得 将i到k合为一堆的最小得分(a堆)+将k+1到j合为一堆的最小得分(b堆)+将a堆与b堆合并的得分 < 将i到j合为一堆的最小得分 时,更新dp[i][j]
即dp1[i][j]=min(dp1[i][k]+dp1[k+1][j]+将a堆与b堆合并的得分,dp1[i][j])
所以现在唯一的未知量就是将a堆与b堆合并的得分了 。
观察到,a堆的石子个数使a[i]+a[i+1]+……+a[k],b堆的石子个数是a[k+1]+a[k+2]+……+a[j] , 所以将a堆与b堆合并的得分=a[i]+a[i+1]+…