前段时间遇到石子合并问题,看着题解A了,以为学会了区间DP,再次遇到能量项链这个问题的时候大脑还是一片空白,只能重新认识一下区间动态规划了。
翻过很多博客,基本上都是题解,真正对区间动态规划本身的讲解几乎没有,可能是我没找到吧。
区间动态规划,顾名思义,就是动态规划过程中求一个区间的最优解。通过将一个大的区间分为很多个小的区间,求其小区间的解,然后一个一个的组合成一个大的区间而得出最终解,有没有发现,这完全就是分治的思想。
动态规划三部曲:
1.状态:用dp[i][j]表示区间(i,j)的最优解
2.状态转移:
最常见的写法: dp[i][j]=max/min(dp[i][j],dp[i][k]+dp[k+1][j]+something)
理解:区间(i,j)的最优解就等于 区间(i,j) 同 (i,k)和区间(k+1,j)合并后的值 比谁更优。
3.初始化: dp[i][i]=0/other (i=1->n)
为什么这么做呢?很好理解,就是只包含自己本身的时候,也就是区间长度为1,值肯定是确定的。
怎么从小到大的合并区间呢?
有两种最常见的写法:
第一种:
for(r=2;r<=n;r++)//r:区间右端点,l:区间左端点
for(l=r-1;l>=1;l--)
for(k=l;k
第二种:
for (int len=2;len<=n;len++)//len:长度 ,l:左区间断点,r:又区间端点
for (int l=1;l+len-1<=n;l++) //保证(l,r)的长度为len,且不越界
{
int r=l+len-1;
for (int k=l;k<=r;k++)
dp[l][r]=max/min(dp[l][r],dp[l][k]+dp[k+1][r]+something)
}
第一种是个人比较喜欢也是很容易想到的。理解起来很简单:遍历以r为终点,从1到r的所有子区间。
这种做法的一个好处就是dp[l][r]存的一定是r为右区间的每一步的最优解。
第二种是用的人数最多的。 理解:从区间长度为2--n,每次遍历固定长度的区间。比如,len=2时,遍历(1,2)(2,3)..(n-1,n)。这样也能遍历完所有的情况。
当然还有其他写法,例如:
for (int len=1;len<=n;len++) //枚举长度
for (int l=1;l
思维都大同小异,只要保证是从小区间遍历到大区间就没问题。
再来看石子合并问题
在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.
输入格式:
数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.
输出格式:
输出共2行,第1行为最小得分,第2行为最大得分.
输入样例#1:
4 4 5 9 4
输出样例#1:
43 54
第一眼看上去就是个贪心问题,每次合并最大的两个,这么想就错了。
8 4 6 3 5 贪心的结果为62,而最优解为60
每次进行合并的时候合并一个区间。
设dp[i][j]为合并i-j区间的石子的最优方案即最小花费,用k去划分石子数去进行状态转移即可。
我们可以得到状态转移方程:
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1])
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1])
sum[i]表示前i个石子分数之和
#include
using namespace std;
int f1[220][220],f2[220][220];
int main()
{
int n,i,j,k;
int a[210];
int sum[220]={0};
cin>>n;
for(i=1;i<=n;i++)
cin>>a[i],a[i+n]=a[i];
for(i=1;i<=n*2;i++)
sum[i]=sum[i-1]+a[i];
for(int len=2;len<=n;len++)
{
for(i=1;i<=2*n-len+1;i++)
{
j=i+len-1;
f1[i][j]=0x3f3f3f3f;
f2[i][j]=0;
for(k=i;kM) M=f2[i][i+n-1];
}
cout<
代码写的有点复杂了,完全可以少一点行数。
注意一个问题:
这么理解:dp[i][j]为(i,j)的最有解,即每次把最优解保存在区间最右端。
比如: i=1 : dp[1][n-1]为区间(1,n-1)的最优解
i=2 : dp[2][n+1]为区间(2,n+1)的最优解(此题为环形转链,总长度2n)
区间DP的经典问题很多,比如矩阵连乘、整数划分、凸多边形划分、多边形合并、能量项链
最难的点还是状态转移方程
那么最后有一个问题,咱不可能每次都看了题解才恍然大悟吧。
笔者水平有限,暂未习得精华,待深入后再回来补充。