重点在讲斜率优化
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
Dp先入脑
有一个Dp[i]:=min(Dp[j]+(sum[i]-sum[j]+i-j-1-L)^2)的基本思路
预处理可以得到
F[i]:=sum[i]+i
;令
C=L+1
可以简化方程:
Dp[i]:=min(Dp[j]+(F[i]-F[j]-c)^2))
明显这个为O(N^2)的算法,,要再简化;
一维的Dp可以想到
斜率优化
所谓斜率优化,个人的感悟就是:
当 I:1-->n J:1-->n的N^2循环时 可以简化J的循环 把1~N依次入队出队,当i要选择j时,先根据最优策略缩短队列,再在选取队首元素直接作为J,算出Dp[i];
这样做的话每个数进出一次队列 复杂度就变为O(n);
但是这样做必须证明单调性,也就是出队的元素在以后的Dp中不会再被利用
下面说说怎么缩短队列
Dp[i]:=min(Dp[j]+(F[i]-F[j]-c)^2))
中j的选择是决定Dp[i]的大小的 而 f[i]、c这样的常量是不变的(对于同一个i来说)
所以我们把不含j的变量分离出来,方便运算
Dp[i]:=min(Dp[j]+F[j]^2-2F[i]F[j]+2F[j]*c)+F[i]^2+c^2-2*F[i]*c;
假设选j好过选k,
本题为
Dp[j]+F[j]^2-2F[i]F[j]+2F[j]*c<Dp[k]+F[k]^2-2F[i]F[k]+2F[k]*c化简分离i
Dp[j]-Dp[k]+(F[i]-F[k])*(2c+F[j]+F[k])
-------------------------------------------<F[i](*)
2(F[j]-F[k])
简化其为
COM(j,k)<F[i]
也就是说 ‘选j好过选k’当且仅当‘
COM(j,k)<F[i]
成立’
所以如果一个队列的头依次为A,B 如果G(B,A)<
=
F[i] 那么把A删了吧
q[1]:=0;
head:=1;
tail:=1;
for i:=1 to n do
begin
while (head<=tail-1)and(com(q[head],q[head+1])<=sum[i]) do
inc(head);
dp[i]:=dp[q[head]]+(f[i]-f[q[head]]-l-1)*(f[i]-f[q[head]]-l-1);
while (head<=tail-1)and(com(q[tail-1],q[tail])>=com(q[tail],i)) do
dec(tail);
inc(tail);
q[tail]:=i;
end;
end;
这就是所谓的斜率优化