BZOJ 1010 玩具装箱toy(四边形不等式优化DP)(HNOI 2008)

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

输出最小费用

 

题目大意:略。

思路:由题意,令w(i, j) = (j - i + sum{c[i..j]} - L)^2

按照黑书(《算法艺术与信息学竞赛》)中的说法,要满足四边形不等式,只需要:

①j 不变时,f(i) = w(i, j + 1) - w(i, j)单调递减。

②i 不变时,f(j) = w(i + 1, j) - w(i, j)单调递减。

当且仅当①②都成立时,w符合四边形不等式(这题里面这个很好验证,就不写了)。

此时决策单调(虽然我不知道为什么……),解法可以参照《1D/1D动态规划优化初步》,我的解法就是参照这个写的。

 

另外,还有斜率优化的写法(虽然我不会),可以参照http://blog.sina.com.cn/s/blog_5f5353cc0100jx41.html

但是在我跑这样一组数据时(随机生成的):

50 5691358
8467214 9692279 8158876 7023812 4301767 4313397 2378849 778746 2288987 3052905 7228328 6855441 4280591 1058424 889808 530342 9432467 761829 3969985 3193649 8070892 711348 5821773 3062537 6891494 5695393 3803579 6418375 1578074 2363659 6477728 533247 6025318 8151154 2442635 1113076 506233 3594564 8277434 1584861 2276145 4442621 7105356 8729441 7283702 1250929 5133264 9233541 5267813 2889179

答案与我的程序跑出的结果136165306871056不一样。稍微目测了一下,上面斜率优化的代码越界了(我不知道怎么改了……)

w(i, j)的极限可高达(50000 - 1 + 50000 * 10^7 - 0)^2,超出了64位整数的范围(虽说似乎没有这样的大数据)。

 

本人的代码在修改后应该是不会存在越界的问题的(避免了两个较大数的乘法,可以参照代码里面的check函数)。

PS:答案一定不会大于50000 * (10^7 - 0)^2 = 5 * 10^18(就是每个玩具一个容器),比2^63-1稍微小那么一点。

 

代码(288MS):

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #include <iostream>
 5 using namespace std;
 6 typedef long long LL;
 7 
 8 const int MAXN = 50010;
 9 
10 LL sum[MAXN], dp[MAXN];
11 int c[MAXN], n, L;
12 
13 LL sqrt_cost(int i, int j) {
14     return j - i + sum[j] - sum[i - 1] - L;
15 }
16 
17 LL cost(int i, int j) {
18     LL res = sqrt_cost(i, j);
19     return res * res;
20 }
21 /*
22 bool check(int pos, int a, int b) {
23     return dp[a] + cost(a + 1, pos) >= dp[b] + cost(b + 1, pos);
24 }
25 */
26 bool check(int pos, int a, int b) {
27     LL aa = dp[a], bb = sqrt_cost(a + 1, pos), cc = dp[b], dd = sqrt_cost(b + 1, pos);
28     return (cc - aa - 1) / (bb - dd) + 1 <= bb + dd;
29 }
30 
31 int binary_search(int st, int ed, int a, int b) {
32     int l = st, r = ed;
33     while(l < r) {
34         int mid = (l + r) >> 1;
35         if(!check(mid, a, b)) l = mid + 1;
36         else r = mid;
37     }
38     return l;
39 }
40 
41 struct Node {
42     int pos, best;
43     Node() {}
44     Node(int pos, int best): pos(pos), best(best) {}
45 } stk[MAXN];
46 int top;
47 
48 LL solve() {
49     int v = 1;
50     stk[++top] = Node(1, 0);
51     for(int i = 1; i <= n; ++i) {
52         if(v < top && stk[v + 1].pos <= i) ++v;
53         int p = stk[v].best;
54         dp[i] = dp[p] + cost(p + 1, i);
55 
56         while(v < top && check(stk[top].pos, stk[top].best, i))
57             --top;
58         int r = stk[top + 1].pos ? stk[top + 1].pos + 1 : n + 1;
59         int t = binary_search(max(stk[top].pos, i) + 1, r, stk[top].best, i);
60         if(t <= n) stk[++top] = Node(t, i);
61     }
62     return dp[n];
63 }
64 
65 int main() {
66     scanf("%d%d", &n, &L);
67     for(int i = 1; i <= n; ++i) scanf("%d", &c[i]);
68     for(int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + c[i];
69     cout<<solve()<<endl;
70 }
View Code

 

posted @ 2014-08-19 18:07  Oyking  阅读(518)  评论(0编辑  收藏  举报