bzoj1010[HNOI2008]玩具装箱toy - dp + 斜率优化
1010: [HNOI2008]玩具装箱toy
Time Limit: 1 Sec Memory Limit: 162 MBDescription
P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京。他使用自己的压缩器进行压
缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中。P教授有编号为1...N的N件玩具,第i件玩具经过
压缩后变成一维长度为Ci.为了方便整理,P教授要求在一个一维容器中的玩具编号是连续的。同时如果一个一维容
器中有多个玩具,那么两件玩具之间要加入一个单位长度的填充物,形式地说如果将第i件玩具到第j个玩具放到一
个容器中,那么容器的长度将为 x=j-i+Sigma(Ck) i<=K<=j 制作容器的费用与容器的长度有关,根据教授研究,
如果容器长度为x,其制作费用为(X-L)^2.其中L是一个常量。P教授不关心容器的数目,他可以制作出任意长度的容
器,甚至超过L。但他希望费用最小.
Input
第一行输入两个整数N,L.接下来N行输入Ci.1<=N<=50000,1<=L,Ci<=10^7
Output
输出最小费用
Sample Input
3
4
2
1
4
Sample Output
一道很好的练习斜率优化的题目 (大家都这么说~~)
dp 方程很容易看出 令 sum[i] = $\sum_1^j C[j]$
$f[i] = min(f[i], f[j] + (sum[i] - sum[j] + i - j - 1 - L) ^ 2)$
我们来对上面的式子进行化简
令s[i] = sum[i] + i
所以$f[i] = f[j] + (s[i] - s[j] - (L +1) ^ 2)$
$= f[j] + s[i] ^ 2 - 2 * s[i] * (s[j] + L + 1) + (s[j] + L + 1) ^ 2$
我们进行一下移项
$f[j] + (s[j] + L + 1) ^ 2 = s[i] * 2 * (s[j] + L + 1) + (f[i] - s[i] ^ 2)$
于是这个式子就变为了 y = kx + b 的形式
即$ y = f[j] + (s[j] + L + 1) ^ 2, k = s[i], x = 2 * (s[j] + L + 1) , b = f[i] - s[i]^2$
我们可以设点$j = (2 * (s[j] + L + 1), f[j] + (s[j] + L + 1) ^ 2)$
那么原来从j 到 i 的转移方程就变为了已知点j 和斜率 s[i], f[i]即为截距
而因为s[i] 是单调递增的,我们便可以用一个单调队列来维护
假设这是我们现有的队列中对应的凸包
当我们要更新i时
对应直线斜率是s[i]
可以想象, 如果凸包最前面的直线的斜率是小于s[i]的,那么左边的点对应的截距也会大,因此我们可以将对首的点弹出
当f[i]更新后,我们要将i点放入凸包中
就用同样的查找凸包操作,从队尾弹出即可
我写的时候因为把 dp 数组设为了 long long 结果就炸了精度,调了老半天,一定小心 TAT!!
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 #define LL long long 6 7 using namespace std; 8 9 const int MAXN = 1e5 + 10; 10 LL N; 11 double L; 12 double sum[MAXN]; 13 double s[MAXN]; 14 double f[MAXN]; 15 LL c[MAXN]; 16 int q[MAXN]; 17 int head = 0, tail = 0; 18 struct node { 19 double y; 20 double x; 21 } t[MAXN]; 22 inline LL read() 23 { 24 LL x = 0, w = 1; char ch = 0; 25 while(ch < '0' || ch > '9') { 26 if(ch == '-') { 27 w = -1; 28 } 29 ch = getchar(); 30 } 31 while(ch >= '0' && ch <= '9') { 32 x = x * 10 + ch - '0'; 33 ch = getchar(); 34 } 35 return x * w; 36 } 37 38 double cal(int a, int b) 39 { 40 return (t[a].y - t[b].y) / (t[a].x - t[b].x); 41 } 42 int main() 43 { 44 memset(f, 0x3f, sizeof f); 45 f[0] = 0; 46 N = read(), L = read(); 47 for(int i = 1; i <= N; i++) { 48 c[i] = read(); 49 sum[i] = sum[i - 1] + c[i]; 50 s[i] = sum[i] + i; 51 } 52 memset(f, 0x3f, sizeof f); 53 f[0] = 0; 54 t[0].x = 2 * (L + 1); 55 t[0].y = (L + 1) * (L + 1); 56 // cout<<t[0].x<<" "<<t[0].y<<endl; 57 tail = 1; 58 q[0] = 0; 59 for(int i = 1; i <= N; i++) { 60 while(head + 1 < tail && cal(q[head], q[head + 1]) <= s[i]) { 61 head++; 62 } 63 int j = q[head]; 64 f[i] = f[j] + (s[i] - s[j] - (L + 1)) * (s[i] - s[j] - (L + 1)); 65 t[i].x = 2 * (s[i] + L + 1); 66 t[i].y = f[i] + (s[i] + L + 1) * (s[i] + L + 1); 67 while(tail > head + 1 && cal(q[tail - 1], i) < cal(q[tail - 1], q[tail - 2])) { 68 tail--; 69 } 70 q[tail++] = i; 71 } 72 printf("%lld\n", (LL)f[N]); 73 return 0; 74 } 75 76 /* 77 5 4 78 79 3 80 81 4 82 83 2 84 85 1 86 87 4 88 */