四边形不等式优化动态规划学习

 学习了四边形不等式,这类题的的方程通常是这样的:

 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)中枚举

 

加入四边形优化的代码也不难,和基本的没有什么区别,只需要在枚举决策的时候变动下就好。

 

伪代码模板
 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],这样就做到了优化程序。

 

合并石子四边形不等式优化
 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/

POJ 1160
 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 }

 

posted @ 2012-06-03 16:52  守護N1身边  阅读(589)  评论(1编辑  收藏  举报