HDU 2829 斜率优化DP Lawrence

题意:n个数之间放m个障碍,分隔成m+1段。对于每段两两数相乘再求和,然后把这m+1个值加起来,让这个值最小。

设:

d(i, j)表示前i个数之间放j个炸弹能得到的最小值

sum(i)为前缀和,cost(i)为前i个数两两相乘之和。

则有状态转移方程:

设0 ≤ l < k < i,且k比l更优,有不等式:

整理得到,注意不等号方向:

 最后变成了斜率的形式,下面就用一个队列维护即可。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 const int maxn = 1000 + 10;
 8 
 9 int n, m;
10 
11 int sum[maxn], cost[maxn];
12 int d[maxn][maxn];
13 
14 int head, tail;
15 int Q[maxn];
16 
17 int j;
18 
19 int inline Y(int i)
20 {
21     return d[i][j-1] + sum[i] * sum[i] - cost[i];
22 }
23 
24 int inline DY(int x1, int x2) { return Y(x2) - Y(x1); }
25 
26 int inline DX(int x1, int x2) { return sum[x2] - sum[x1]; }
27 
28 int inline DP(int x1, int x2) { return d[x1][j-1] + cost[x2] - cost[x1] - sum[x1]*(sum[x2]-sum[x1]); }
29 
30 int main()
31 {
32     while(scanf("%d%d", &n, &m) == 2 && n)
33     {
34         for(int i = 1; i <= n; i++) scanf("%d", sum + i);
35         for(int i = 2; i <= n; i++) sum[i] += sum[i - 1];
36         for(int i = 2; i <= n; i++) cost[i] = cost[i-1] + (sum[i]-sum[i-1])*sum[i-1];
37 
38         for(int i = 1; i <= n; i++) d[i][0] = cost[i];
39         for(j = 1; j <= m; j++)
40         {
41             head = tail = 0;
42             Q[tail++] = 0;
43             for(int i = 1; i <= n; i++)
44             {
45                 while(head + 1 < tail && DY(Q[head], Q[head+1]) <= sum[i] * DX(Q[head], Q[head+1])) head++;
46                 d[i][j] = DP(Q[head], i);
47                 while(head + 1 < tail && DY(Q[tail-1], i) * DX(Q[tail-2], Q[tail-1]) <= DY(Q[tail-2], Q[tail-1]) * DX(Q[tail-1], i)) tail--;
48                 Q[tail++] = i;
49             }
50         }
51 
52         printf("%d
", d[n][m]);
53     }
54 
55     return 0;
56 }
代码君

贴一个四边形不等式优化的代码对比一下:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 const int maxn = 1000 + 10;
 8 const int INF = 0x3f3f3f3f;
 9 
10 int n, m;
11 int a[maxn], sum[maxn], cost[maxn];
12 int d[maxn][maxn], s[maxn][maxn];
13 
14 
15 int inline w(int k, int j)
16 {
17     return cost[j] - cost[k-1] - sum[k-1] * (sum[j] - sum[k-1]);
18 }
19 
20 int main()
21 {
22     while(scanf("%d%d", &n, &m) == 2 && n)
23     {
24         m++;
25 
26         for(int i = 1; i <= n; i++) scanf("%d", a + i);
27         for(int i = 1; i <= n; i++) sum[i] = sum[i - 1] + a[i];
28         for(int i = 2; i <= n; i++) cost[i] = cost[i-1] + sum[i-1] *  a[i];
29 
30         memset(d, 0, sizeof(d));
31         for(int i = 1; i <= m; i++)
32             for(int j = i + 1; j <= n; j++) d[i][j] = INF;
33 
34         for(int i = 1; i <= n; i++) { d[1][i] = cost[i]; s[1][i] = 0; }
35         for(int i = 2; i <= m; i++)
36         {
37             s[i][n+1] = n;
38             for(int j = n; j >= i; j--)
39             {
40                 for(int k = s[i-1][j]; k <= s[i][j+1]; k++)
41                 {
42                     int tmp = d[i-1][k] + w(k+1, j);
43                     if(tmp < d[i][j])
44                         { d[i][j] = tmp; s[i][j] = k; }
45                 }
46             }
47         }
48 
49         printf("%d
", d[m][n]);
50     }
51 
52     return 0;
53 }
代码君
原文地址:https://www.cnblogs.com/AOQNRMGYXLMV/p/4693248.html