环形区间DP本质上还是求解链形区间DP,只是一个有n个节点的环可以拆分成n个不同的链。如果分别计算每个链的结果,那么本题一定会超时。优化策略:将一个链复制一份并连接在其尾部,形成一个2n个节点的“长链”。上述n条不同的链都能在该“长链”中找到。枚举len的时候只需要枚举到len = n即可。对于较小的n,我们还是可以采用DP朴素O(n^3)来做。
假设圆形石子长度为n,基于动态规划的问题求解,可以采用集合的方式求解,区间DP问题一般采用二维空间计算。对于问题需要求最大最小两个值我们可以开两个数组f(最大值),s(最小值)。因为需要将链再复制一次,所以数组开空间的时候要开到n*2,即f[n<<1][n<<1],s[n<<1][n<<1]。除此之外我们还要开两个数组,一个数组e用来存储我们输入每一堆石子的,即e[n<<1],数组c用来存储前缀和,便于计算两堆石子合并时的花费。因为求最大值所以数组s里的每个值都初始化为0x3f3f3f3f(一个极大值,视情况而定),由于石子合并的花费不会为负数,所以f数组不用管。
状态转移方程的计算我们可以用到分集合的方式,对于f[i][j](j>=i)来讲,它代表的是合并从i到j这好几堆石子的最大花费。不管从i到j有多少堆石子,合并到最后一定是左边剩一堆,右边剩一堆,然后再合并成一堆。所以我们就需要枚举断点。所以我们的转态转移方程就有了f[i][j]=max(f[i][k]+f[k+1][j]+c[j]-c[i-1],f[i][j]),k是一层循环,从i到j-1。如果len==1,则f[i][i]=0。f[i][i]的状态是合理的,自己和自己合并的花费是0。如果len!=0则进行状态转移。直接看代码:
#include<bits/stdc++.h>
using namespace std;
const int N=410;
int n;
int f[N][N],s[N][N];
int e[N],c[N];