bzoj1010[HNOI2008]玩具装箱toy - dp + 斜率优化

1010: [HNOI2008]玩具装箱toy

Time Limit: 1 Sec  Memory Limit: 162 MB

Description

  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

5 4
3
4
2
1
4

Sample Output

1

 

一道很好的练习斜率优化的题目 (大家都这么说~~)

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 */
View Code

 

posted @ 2018-02-01 15:17  大财主  阅读(184)  评论(0编辑  收藏  举报