[HNOI2008]玩具装箱toy
1010: [HNOI2008]玩具装箱toy
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 3647 Solved: 1242
[Submit][Status][Discuss]
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
3
4
2
1
4
Sample Output
1
HINT
Source
Solution
斜率优化DP。
这一题学到好多, 首先是斜率优化DP, 然后是单调队列维护。 其实这两个知识很早以前我就看过了, 只是一直没有什么时间来学, 今天花了大约2个多小时细细的体会了一下, 豁然开朗的感觉。LOL!!
首先:
这道题一开始一定能想到一个O(N^2)的DP方程:
dp[i] = dp[j] + min(sum[i] - sum[j] + i - j - 1 + L)^2
dp[i] = min(dp[j] + (sum[i] - sum[j] + i - j - 1 + L)^2)
接着化成斜率的形式什么的,可以用笔推导一下, 过程虽然繁琐, 但是很好想的:)
再讲讲写率优化吧:
斜率优化,就是把决策的关系写成(Y1-Y2)/(x1-x2)的斜率式,然后用队列维护。
队列中的决策需要保证两个性质 : ① j1<j2<j3<j4....②slope(j1,j2)<slope(j2,j3)<slope(j3,j4)。
关于单调队列:
1、对某个阶段i进行决策时候,首先判断用队头决策是否比用队头+1决策优,如果不优,就出队。直到队头决策优。
2、计算阶段i的值。
3、判断 队尾和i的斜率 与 队尾-1和i的斜率 哪个大。如果 队尾-1 大,那就将队尾删除。直到队尾斜率大,然后i入队。
Code
View Code
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <cstring> 5 #define maxn 50050 6 using namespace std; 7 int d[maxn], head = 1, tail = 1, L, n; 8 long long dp[maxn], sum[maxn]; 9 10 bool slope(int i, int j, int k) { 11 return ((dp[i] + sum[i] * sum[i]) - (dp[k] + sum[k] * sum[k])) * (sum[i] - sum[j]) < ((dp[i] + sum[i] * sum[i]) - (dp[j] + sum[j] * sum[j])) * (sum[i] - sum[k]); 12 } 13 14 long long cal(int i, int j) { 15 return dp[j] + (sum[i] - sum[j] - L) * (sum[i] - sum[j] - L); 16 } 17 18 void init() { 19 int i, j; 20 scanf("%d%d", &n, &L); 21 L++; 22 for (i = 1; i <= n; i++) { 23 scanf("%d", &j); 24 sum[i] = sum[i - 1] + j + 1; 25 } 26 } 27 28 void solve() { 29 int i; 30 d[1] = 0; 31 for (i = 1; i <= n; i++) { 32 while (head < tail && cal(i, d[head]) > cal(i, d[head+1])) head++; 33 dp[i] = cal(i, d[head]); 34 while (head < tail && slope(i, d[tail - 1], d[tail])) tail--; 35 d[++tail] = i; 36 } 37 printf("%lld\n", dp[n]); 38 } 39 40 int main() { 41 init(); 42 solve(); 43 return 0; 44 }
I come, I see, I conquer!