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\n", 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\n", d[m][n]); 50 } 51 52 return 0; 53 }