环形石子合并问题及四边形不等式优化

环形石子合并问题及四边形不等式优化_第1张图片

        环形区间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
 
using namespace std;
 
const int N=410;
 
int n;
 
int f[N][N],s[N][N];
int e[N],c[N];
 
int main(){
    
    scanf("%d",&n);
    
    for(int i=1;i<=n;++i) scanf("%d",&e[i]),e[i+n]=e[i];
    
    for(int i=1;i<=n<<1;++i) c[i]=c[i-1]+e[i];
    
    memset(s,0x3f,sizeof s);
    
    for(int len=1;len<=n;++len){
        for(int i=1;i+len-1<=n<<1;++i){
            int j=i+len-1;
            if(len==1) f[i][i]=s[i][i]=0;
            else{
                for(int k=i;k

接下来我们来讲一下四边形不等式的思路。

(一)四边形不等式的定义:
设w是定义在整数集合上的二元函数,对于任意的i<=i’

(二)四边形不等式的单调性
设w是定义在整数集合上的二元函数,如果对任意整数i=w(i’,j),则称w具有单调性。形象的理解为,如果大区间包含小区间,那么大区间的w值比小区间的w值要大。

(三)算法优化阐述
经过实验设计可得,c数组符合四边形不等式和单调性,而且s数组也符合,但f(求最大值)并不符合点调性,但f数组满足一个性质,从i~j这整个区间中[i+1]~[j-1]这个区间合并的花费是固定的,所以f的状态转移方程可以重新表示为f[i][j]=max(f[i+1][j],f[i][j-1])+c[j]-c[i-1]。算法的优化需要用到一个新的数组g用来记录断点,即g[i][j]为合并从i到j这很多石子最大花费的断点。经证g数组也符合四边形不等式,所以可以得到g[i][j-1]<=g[i][j]<=g[i+1][j],g[i][j]=k,所以k的枚举范围就变成了g[i][j-1]<=k<=g[i+1][j]。

(四)单调性证明
1. cost函数符合四边形不等式
给定 i

2.s数组符合四边形不等式
只需要证明s[i][j]+s[i+1][j]<=s[i+1][j]+s[i][j+1],假设s[i+1][j]的最优断点k=x,s[i][j+1]的最优断点k=y。即要证s[i][j]+s[i+1][j+1]<=s[i+1][x]+s[x+1][j]+cost[i+1][j]+s[i][y]+s[y+1][j+1]+cost[i][j+1]。由于s[i][j]的最优解不一定为k=x,则s[i][j]<=s[i][x]+s[x+1][j]+cost[i][j]。同理由于s[i+1][j]的最优解不一定为k=y,则s[i+1][j]<=s[i+1][y]+s[y+1][j]+cost[i+1][j]。又因为cost[i][j]+cost[i+1][j]

3.证明d[i][j-1]<=d[i][j]<=d[i+1][j]成立
先证d[i][j-1]<=d[i][j],设y=d[i][j-1],对任意的x=0,即s[i][j](k=x)>=s[i][j](k=y),也就是说对于s[i][j],任意一个k=x

(五)证明最大值数组f不具有单调性
只需要证明f[i][j]+f[i+1][j]<=f[i+1][j]+f[i][j+1],假设f[i+1][j]的最优断点k=x,s[i][j+1]的优断点k=y。即要证f[i][j]+f[i+1][j+1]<=f[i+1][x]+f[x+1][j]+cost[i+1][j]+f[i][y]+f[y+1][j+1]+cost[i][j+1]。由于f[i][j]的最优解不一定为k=x,所以对于f[i][j]和s[i][x]+s[x+1][j]+cost[i][j]就无法进行比较,由此即可证得,最大值数组f不符合单调性。

(六)时间复杂度分析
对于固定的区间长度len总共的决策只有O(n)个,一共有n个len所以最后的时间复杂度可以被优化成O(n*n),即O(n^2)。
 

#include
using namespace std;
const int N=2010;
 
int n;
 
int f[N][N],s[N][N];
int e[N],c[N];
int d[N][N];
 
int main(){
	
	scanf("%d",&n);	
	for(int i=1;i<=n;++i) scanf("%d",&e[i]),e[i+n]=e[i];	
	for(int i=1;i<=n<<1;++i) d[i][i]=i,c[i]=c[i-1]+e[i];
	
	memset(s,0x3f,sizeof s);
	
	for(int len=1;len<=n;++len){
		for(int i=1;i+len-1<=n<<1;++i){
			int j=i+len-1;
			if(len==1) f[i][j]=s[i][j]=0;
			else{
			    f[i][j]=max(f[i][j-1],f[i+1][j])+c[j]-c[i-1];
					
				for(int k=d[i][j-1];k<=d[i+1][j];k++){
					int tmp=s[i][k]+s[k+1][j]+c[j]-c[i-1];
							
					if(tmp

你可能感兴趣的:(动态规划,算法,c++)