您的位置:首页 > 其它

(dp的斜率优化)玩具装箱 解题报告

2017-12-20 16:29 381 查看
某些状态转移方程可以写成这样的形式:f[i]=min/max(一个与i有关的值),设决策变量为k(即在k处取得最优值),可以设法把不等式化为u(j,k)<=v(i);其中u(j,k)近似于斜率的形式,即(yj-yk)/(xj-xk),v(i)是与i有关的量。

我们来看看下面这题:

[HNOI2008]玩具装箱toy
Time Limit:1000MS  Memory Limit:165536K
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

用f[i]表示前i个玩具装箱的最小费用,朴素的方程比较容易想:f[i]=min(f[j]+(i-j+sum[i]-sum[j]-L-1)^2);
因为X=i-j-1+sigma(Ck),(注意了:f[j]是前j个玩具装箱,所以X是从j+1到i,千万要记得是i-j-1而不是i-j),所以(X-L)^2就是(i-j+sum[i]-sum[j]-L-1)^2啦!
时间复杂度是O(n^2),必然过不了。

设决策变量为k,则f[k]+(i-k+sum[i]-sum[k]-L-1)^2<=f[j]+(i-j+sum[i]-sum[j]-L-1)^2,然后化简……
好吧看你也不想化……其实我第一次化简时也做了很久,如果不熟练确实比较复杂。
这里先别急着拆括号,看:i+sum[i],j+sum[j],k+sum[k],当然可以看作一个整体g(i),使得g(i)=sum[i]+i;
L是常量,可以令l=L+1,简化式子。

那么f[k]+(g(i)-g(k)-l)^2<=f[j]+(g(i)-g(j)-l)^2,这样是不是好看多了?
这时拆开来,消掉g(i)^2,则:
f[k]-2*g(i)*(g(k)+l)+(g(k)+l)^2<=f[j]-2*g(i)*(g(j)+l)+(g(j)+l)^2;
前面说到要把与i有关的量移到右边,那么:
f[k]-f[j]+(g(k)+l)^2-(g(j)+l)^2<=2*g(i)*(g(k)-g(j))
两边同除以2*(g(k)-g(j)),得
(f[k]+(g(k)+l)^2)-(f[j]+(g(j)+l)^2) / 2*(g(k)-g(j))<=g(i)
这样子左边就化成了类似于斜率的形式。
给出一个结论,证明大家另查资料:
设左边为slope[j,k],g(i)单调递增时,若slope[j,k]>slope[k,t],则k一定不是最优解;同理,g(i)单调递减时,若slope[j,k]<slope[k,t],则k一定不是最优解。
可以形象地理解为,g(i)单调递增时,斜率单调递增,图形成下凸状;g(i)单调递减时,斜率单调递减,图形成下凸状;


那么实现的时候,就可以用一个单调队列维护,在入队时,对于新加入的点i,持续维护slope[q[tail-1],q[tail]]>(或小于)slope[q[tail],q[i]],不符合条件则删掉队尾的点(tail--)直到符合条件。这样子,在dp时就不用枚举j,用i枚举一遍就可以了,从而把O(n^2)的复杂度降到O(n)。
不过在队尾维护之前,还有队首的维护,即当slope[q[head],q[head+1]]<=g(i)时,q[head+1]一定优于q[head],去除队首的点。维护后队首的点即为决策变量,故状态转移方程变为f[i]=f[q[head]]+(g(i)-g(head)-l)。
其它题目也是上述的思路啦,好好消化一下吧!

附上代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n;
long long l;
long long sum[50001];
long long f[50001];
int que[50001];
long long sqr(long long x)
{
return x*x;
}
double y(int i)
{
return double(f[i]+sqr(sum[i]+l+1));
}
double x(int i)
{
return double(2*sum[i]);
}
int main()
{
scanf("%d",&n);
scanf("%I64d",&l);
for (int i=1;i<=n;i++)
{
long long x;
scanf("%I64d",&x);
sum[i]=sum[i-1]+x;
}
for (int i=1;i<=n;i++) sum[i]+=(long long)(i);
int head=0;
int tail=0;
for (int i=1;i<=n;i++)
{
while (head<tail&&(y(que[head+1])-y(que[head]))/(x(que[head+1])-x(que[head]))<=double(sum[i])) head++; //队首维护
f[i]=f[que[head]]+sqr(sum[i]-sum[que[head]]-l-1);
while (head<tail&&(y(i)-y(que[tail]))/(x(i)-x(que[tail]))<(y(que[tail])-y(que[tail-1]))/(x(que[tail])-x(que[tail-1]))) tail--; //队尾维护
que[++tail]=i;
}
printf("%I64d",f
);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法 dp 优化