首先打广告:http://www.cnblogs.com/ronaflx/archive/2011/02/05/1949278.html
我写的斜率优化的解题报告,本文有一些题目在上面的斜率的总结中也有列出,欢迎指错
总体来说我做过的四边形的题目转移代价分为几类
第一类:石子合并类型:NOI95的石子合并的四边形优化
例如最优二叉树也是这个类型的题目
http://www.cnblogs.com/ronaflx/archive/2010/10/20/1856971.html 是该题的解题报告
也是是我的第一个四边形的题目
转移方程为dp[i][j] = min(dp[i][k] + dp[k + 1][j] + cost[i][j]);
HOJ 2952 多校的时候cai学长出的环形四边形不等式DP
1 #include < iostream >
2 #include < cstring >
3 #include < cstdio >
4 #include < algorithm >
5 using namespace std;
6 const int N = 2010 ;
7 const int INF = 100000000 ;
8 int dp[N][N], s[N][N], x[N], sum[N];
9 int cost[N][N];
10 int DP( int n)
11 {
12 for ( int i = 1 ;i <= n * 2 ;i ++ )
13 {
14 dp[i][i] = 0 ;
15 s[i][i] = i;
16 }
17 for ( int len = 2 ;len <= n;len ++ )
18 {
19 for ( int i = n * 2 - len + 1 ;i > 0 ;i -- )
20 {
21 int j = i + len - 1 ;
22 int a = s[i][j - 1 ], b = min(s[i + 1 ][j], j - 1 );
23 dp[i][j] = INF;
24 for ( int k = a;k <= b;k ++ )
25 {
26 int c = cost[i][j];
27 if (dp[i][j] > dp[i][k] + dp[k + 1 ][j] + c)
28 {
29 dp[i][j] = dp[i][k] + dp[k + 1 ][j] + c;
30 s[i][j] = k;
31 }
32 }
33 }
34 }
35 int ans = INF;
36 for ( int i = 1 ;i + n - 1 <= 2 * n;i ++ )
37 ans = min(ans, dp[i][i + n - 1 ]);
38 return ans;
39 }
40
41 int main()
42 {
43 int n;
44 while (scanf( " %d " , & n) == 1 )
45 {
46 for ( int i = 1 ;i <= n;i ++ )
47 {
48 scanf( " %d " , & x[i]);
49 x[i + n] = x[i];
50 }
51 for ( int i = 1 ;i <= 2 * n;i ++ )
52 sum[i] = sum[i - 1 ] + x[i];
53 for ( int i = 1 ;i <= 2 * n;i ++ )
54 for ( int j = i;j <= 2 * n;j ++ )
55 cost[i][j] = sum[j] - sum[i - 1 ];
56 printf( " %d\n " , DP(n));
57 }
58 return 0 ;
59 }
第二类:dp[i][j] 形象的表示转移就是:前i个数被j个挡板隔开的最小代价
一下三道例题就是该转移类型的题目
转移方程:dpi][j] = min(dp[i][j - 1] + cost[k + 1][i]);
HOJ 1005 fast food 经典题目
POJ1160 也是一样的四边形不等式题目数据比较弱,所以很容易。AC不过还是出了一些错误的
写了两个版本的,开始因为懒的改了,把状态的顺序反着写的(就是和一般四边形不等式的状态ij换了一个位置,强烈建议不要这么写,给自己找麻烦)结果错了,然后果断重新写了一个一般形式的AC了……回来又把原来的改了,这个题和HOJ 1005是一样的题。
对于任意的a <= b <= c <= d 都满足 cost[a][c] + cost[b][d] <= cost[a][d] + cost[b][c](太经典了,证明略去)
代码如下
1 #include < iostream >
2 #include < cstring >
3 #include < cstdio >
4 using namespace std;
5 const int N = 310 ;
6 const int P = 32 ;
7 const int INF = 100000000 ;
8 int dp[N][P], cost[N][N];
9 int x[N], s[N][P];
10 void preprocess( int n)
11 {
12 for ( int i = 1 ;i <= n;i ++ )
13 {
14 cost[i][i] = 0 ;
15 for ( int j = i + 1 ;j <= n;j ++ )
16 {
17 int mid = (i + j) / 2 ;
18 cost[i][j] = cost[i][j - 1 ] + x[j] - x[mid];
19 cost[j][i] = cost[i][j];
20 }
21 }
22 }
23
24 int DP( int n, int m)
25 {
26 memset(dp, 0 , sizeof (dp));
27 for ( int i = 1 ;i <= n;i ++ )
28 {
29 dp[i][ 1 ] = cost[ 1 ][i];
30 s[i][ 1 ] = 0 ;
31 }
32 for ( int i = 2 ;i <= m;i ++ )
33 {
34 for ( int j = n;j >= i;j -- )
35 {
36 dp[j][i] = INF;
37 int a = s[j][i - 1 ], b = s[j + 1 ][i];
38 if (j == n)
39 {
40 a = i - 1 ;
41 b = n;
42 }
43 for ( int k = a;k <= b;k ++ )
44 {
45 if (dp[j][i] > dp[k][i - 1 ] + cost[k + 1 ][j])
46 {
47 dp[j][i] = dp[k][i - 1 ] + cost[k + 1 ][j];
48 s[j][i] = k;
49 }
50 }
51 }
52 }
53 return dp[n][m];
54 }
55 int main()
56 {
57 int n, m;
58 while (scanf( " %d %d " , & n, & m) == 2 )
59 {
60 for ( int i = 1 ;i <= n;i ++ )
61 scanf( " %d " , & x[i]);
62 preprocess(n);
63 printf( " %d\n " , DP(n, m));
64 }
65 return 0 ;
66 }
HDU 3480
之前写过这题的斜率优化解法,是个非常好的题目,这题也能四边形不等式解,最重要的是四边形不等式的解法写起来更简单,虽然效率低了一些
38xxMS,不排除我写的搓的可能性……
这个比较好证明可以满足四边形不等式
w[a][c] + w[b][d] = x[a]^2 + x[b]^2 + x[c]^2 + x[d]^2 - 2 * (x[a] * x[c]) - 2 * (x[b] * x[d])
w[a][d] + w[b][c] = x[a]^2 + x[b]^2 + x[c]^2 + x[d]^2 - 2 * (x[a] * x[d]) - 2 * (x[b] * x[c])
相减 得 2 * (x[a] * (x[d] - x[c]) + x[b] * (x[c] - x[d])) <= 0
满足四边形不等式条件
PS:此题很双,内存巨大无比,可以滚动数组但是果然还是大内存爽呀
代码如下
1 #include < iostream >
2 #include < cstring >
3 #include < cstdio >
4 #include < algorithm >
5 using namespace std;
6 const int N = 10001 ;
7 const int M = 5001 ;
8 const int INF = 100000000 ;
9 int dp[N][M], s[N][M], a[N];
10
11 int cost( int i, int j)
12 {
13 return (a[i] - a[j]) * (a[i] - a[j]);
14 }
15
16 int DP( int n, int m)
17 {
18 for ( int i = 1 ;i <= n;i ++ )
19 {
20 dp[i][ 1 ] = cost( 1 , i);
21 s[i][ 1 ] = 0 ;
22 }
23 for ( int j = 2 ;j <= m;j ++ )
24 for ( int i = n;i >= j;i -- )
25 {
26 dp[i][j] = INF;
27 int a = s[i][j - 1 ], b = s[i + 1 ][j];
28 if (i == n)
29 {
30 a = j - 1 ;
31 b = n;
32 }
33 for ( int k = a;k <= b;k ++ )
34 {
35 int c = cost(k + 1 , i);
36 if (dp[i][j] > dp[k][j - 1 ] + c)
37 {
38 s[i][j] = k;
39 dp[i][j] = dp[k][j - 1 ] + c;
40 }
41 }
42 }
43 return dp[n][m];
44 }
45
46 int main()
47 {
48 int n, m, cases;
49 scanf( " %d " , & cases);
50 for ( int k = 1 ;k <= cases;k ++ )
51 {
52 scanf( " %d %d " , & n, & m);
53 for ( int i = 1 ;i <= n;i ++ )
54 scanf( " %d " , & a[i]);
55 sort(a + 1 , a + 1 + n);
56 printf( " Case %d: %d\n " ,k, DP(n, m));
57 }
58 return 0 ;
59 }
同样是斜率优化那篇文章写的题目 HDU2829Lawrence也可以用四边形不等式优化
证明过程可以和上题一样把preprocess过程的计算式展开,然后相减就可以证明了
1 #include < iostream >
2 #include < cstring >
3 #include < cstdio >
4 using namespace std;
5 const int N = 1002 ;
6 const int P = 1002 ;
7 const int INF = 100000000 ;
8 int dp[N][P], cost[N][N], s[N][P];
9 int x[N], p[N], sum[N], sp[N];
10 void preprocess( int n)
11 {
12 sum[n + 1 ] = 0 ;
13 p[n + 1 ] = 0 ;
14 for ( int i = n;i > 0 ;i -- )
15 {
16 p[i] = x[i] * sum[i + 1 ];
17 sum[i] = sum[i + 1 ] + x[i];
18 sp[i] = sp[i + 1 ] + p[i];
19 }
20 for ( int i = 1 ;i <= n;i ++ )
21 for ( int j = i;j <= n;j ++ )
22 cost[i][j] = sp[i] - sp[j] - (sum[i] - sum[j]) * sum[j + 1 ];
23 }
24 int DP( int n, int m)
25 {
26 memset(dp, 0 , sizeof (dp));
27 for ( int i = 1 ;i <= n;i ++ )
28 {
29 dp[i][ 0 ] = cost[ 1 ][i];
30 s[i][ 0 ] = 0 ;
31 }
32 for ( int j = 1 ;j <= m;j ++ )
33 {
34 for ( int i = n;i >= j;i -- )
35 {
36 dp[i][j] = INF;
37 int a = s[i][j - 1 ], b = s[i + 1 ][j];
38 if (i == n)
39 {
40 a = j - 1 ;
41 b = n;
42 }
43 for ( int k = a;k <= b;k ++ )
44 {
45 if (dp[i][j] > dp[k][j - 1 ] + cost[k + 1 ][i])
46 {
47 s[i][j] = k;
48 dp[i][j] = dp[k][j - 1 ] + cost[k + 1 ][i];
49 }
50 }
51 }
52 }
53 return dp[n][m];
54 }
55 int main()
56 {
57 int n, m;
58 while (scanf( " %d %d " , & n, & m) == 2 && (n + m))
59 {
60 for ( int i = 1 ;i <= n;i ++ )
61 scanf( " %d " , & x[i]);
62 preprocess(n);
63 printf( " %d\n " , DP(n, m));
64 }
65 return 0 ;
66 }
第三类:
之所以把他分类第三类,原因是一个四边形成立的证明我不会……
而且这个代价函数和决策量k有关系,所以我一致怀疑四边形是错误的解法,有待其他人的指点
HDU 3516就是这类的题目
四边形AC了,但是理论有待研究
1 #include < iostream >
2 #include < cstring >
3 #include < cstdio >
4 #include < algorithm >
5 using namespace std;
6 const int N = 1010 ;
7 const int INF = 100000000 ;
8 int dp[N][N], s[N][N], x[N], y[N];
9 int cost( int i, int k, int j)
10 {
11 return y[k] - y[j] + x[k + 1 ] - x[i];
12 }
13 int DP( int n)
14 {
15 for ( int i = 1 ;i <= n;i ++ )
16 {
17 dp[i][i] = 0 ;
18 s[i][i] = i;
19 }
20 for ( int len = 2 ;len <= n;len ++ )
21 {
22 for ( int i = n - len + 1 ;i > 0 ;i -- )
23 {
24 int j = i + len - 1 ;
25 int a = s[i][j - 1 ], b = min(s[i + 1 ][j], j - 1 );
26 dp[i][j] = INF;
27 for ( int k = a;k <= b;k ++ )
28 {
29 int c = cost(i, k, j);
30 if (dp[i][j] > dp[i][k] + dp[k + 1 ][j] + c)
31 {
32 dp[i][j] = dp[i][k] + dp[k + 1 ][j] + c;
33 s[i][j] = k;
34 }
35 }
36 }
37 }
38 return dp[ 1 ][n];
39 }
40
41 int main()
42 {
43 int n;
44 while (scanf( " %d " , & n) == 1 )
45 {
46 for ( int i = 1 ;i <= n;i ++ )
47 scanf( " %d %d " , & x[i], & y[i]);
48 printf( " %d\n " , DP(n));
49 }
50 return 0 ;
51 }
总结:能用四边形的尽量用四边形写,相比于斜率优化代码简洁有优势,而且证明相比于斜率的证明要容易一些,遇到一些大范围的DP,要第一时间验证是否可以四边形优化。
如果两者都可以的话四边形优先吧,应该不会有题变态到卡这点常数吧,而且斜率的计算容易产生溢出,符号笔误之类的事情