前段时间遇到石子合并问题,看着题解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<r;k++)
dp[l][r] = max/min(dp[l][r],dp[l][k]+dp[k+1][r]+something);
第二种:
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-len;l++) //从第i个石子开始
for (int j=l;j<l+len;j++)
dp[l][l+len]=max(dp[l][l+len],dp[l][j]+dp[j+1][l+len])+somethong;
思维都大同小异,只要保证是从小区间遍历到大区间就没问题。