四边形不等式优化动态规划学习
学习了四边形不等式,这类题的的方程通常是这样的:
f[i,j]=opt( f[i,k-1]+f[k,j] )+cost[i,j]
【我一般理解为区间动态规划】
应用条件是:
第一,其中的cost(I,j)是存在单调性的,即当区间[i,j]包含区间[i’,j’]时有cost(i,j)>=cost(i’,j’)
第二,设i<=i’<=j<=j’,有cost(i,j)+cost(i’,j’)<=cost(i’,j)+cost(i,j’)
如果上面两个条件满足的话,那么就有以下的定理:
定理1:
f(I,j)也满足四边形不等式
即设i<=i’<=j<=j’,有f(i,j)+f(i’,j’)<=f(i’,j)+f(i,j’)
定理2:
设g(i,j)为当f(i,j)取最优值时的最大k值(当有多个k满足时,取最大的k)
g(i,j)单调:g(i,j-1)<=g(i,j)<=g(i+1,j)
最后的结论就是:
求f(I,j)的时候决策k不必在[I,j]中枚举,可以在g(i,j-1)与g(i+1,j)中枚举
加入四边形优化的代码也不难,和基本的没有什么区别,只需要在枚举决策的时候变动下就好。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 memset(f,0,sizeof(f)); memset(g,0,sizeof(g)); 2 for (int i=1; i<=n; i++) 3 g[i][i]=i; 4 for (int len=1; len<=n; len++) 5 for (int i=2; i<=n; i++) 6 { 7 j=i+len-1; 8 if (j>n) break; 9 for (int k=g[i][j-1]; k<=g[i+1][j]; k++) 10 if (可以更新) 11 { 12 更新; 13 g[i][j]=k; 14 } 15 }
想要证明凸性也很简单,例如要验证w的凸性,我们知道,这个不等式是有对称性的,也就是说我们可以固定一个变量,例如固定j,直接算出w(I,j+1)-w(I,j)的关于i的表达式,然后观察这个表达式是否有单调性,如果是为递减的,那么w为凸,满足四边形不等式,懒得列出证明了,想要的可以自己百度一下。
做了几道题,先整理了一道,其余的以后补上
【题目一】 Noi 1995 合并石子(题目很好找的)
状态转移方程很好列出来,基本的区间动态规划
f[i,j]=min{f[i,k-1]+f[k,j]}+s[i,j]
其中s[I,j]表示i到j堆石子的累加和,很明显满足四边形不等式,所以f[i,j]也满足凸性[即四边形不等式]
这样我们如果用p[I,j]表示最优决策的话,那么一定有p[i,j-1]<=p[I,j]<=p[I+1,j],这样就做到了优化程序。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 3 4 5 #define MaxN 1024 6 7 #define oo 200000000 8 9 10 11 using namespace std; 12 13 14 15 int n,i,j,k,len; 16 17 int num[MaxN], sum[MaxN]; 18 19 int f[MaxN][MaxN],s[MaxN][MaxN]; 20 21 22 23 int main() 24 25 { 26 27 sum[0] = 0; 28 29 30 31 scanf("%d",&n); 32 33 34 35 for (i=1; i<=n; i++) 36 37 { 38 39 scanf("%d", &num[i]); 40 41 sum[i] = sum[i-1] + num[i]; 42 43 f[i][i] = 0; 44 45 s[i][i] = i; 46 47 } 48 49 50 51 for (len=2; len<=n; len++) 52 53 for (i=1; i<=n-len+1; i++) 54 55 { 56 57 j=i + len - 1; 58 59 f[i][j]=oo; 60 61 62 63 for (k=s[i][j-1]; k<=s[i+1][j]; k++) 64 65 if (f[i][k-1]+f[k][j]+sum[j]-sum[i-1] < f[i][j]) 66 67 { 68 69 f[i][j] = f[i][k-1]+f[k][j]+sum[j]-sum[i-1]; 70 71 s[i][j] = k; 72 73 } 74 75 } 76 77 78 79 printf("%d\n", f[1][n]); 80 81 82 83 return 0; 84 85 }
【题目二】 POJ 1160
题解这个人写的很好,我算是跟着写的,用到了一点儿变形
http://blog.163.com/zjut_nizhenyang/blog/static/169570029201111835039821/
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <algorithm> 3 4 #define oo 1000000000 5 #define MaxN 310 6 7 using namespace std; 8 9 int n,m,i,j; 10 int f[MaxN][MaxN],w[MaxN][MaxN]; 11 int v[MaxN],s[MaxN][MaxN]; 12 13 int main() 14 { 15 16 scanf("%d%d",&n,&m); 17 18 for(i = 1 ; i <= n ; i ++) 19 scanf("%d",&v[i]); 20 for(int i = 1 ; i <= n ; i ++) 21 { 22 w[i][i] = 0; 23 for(int j = i + 1 ; j <= n ; j ++) 24 w[i][j] = w[i][j-1]+v[j]-v[(j+i)/2]; 25 } 26 27 for(i = 1 ; i <= n ; i ++) 28 { 29 f[i][1]=w[1][i]; 30 s[i][1]=0; 31 } 32 33 for(i = 2 ; i <= m ; i ++) 34 { 35 s[n+1][i]=n; 36 for(j = n ; j > i ; j --) 37 { 38 f[j][i] = oo; 39 for (int k = s[j][i-1]; k <= s[j+1][i] ; k ++) 40 if(f[k][i-1]+w[k+1][j]<f[j][i]) 41 { 42 f[j][i]=f[k][i-1]+w[k+1][j];; 43 s[j][i]=k; 44 } 45 } 46 } 47 printf("%d\n",f[n][m]); 48 return 0; 49 }