(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)。
其它题目也是上述的思路啦,好好消化一下吧!
附上代码:
我们来看看下面这题:
[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 ); }
相关文章推荐
- BZOJ 1010 [HNOI2008]玩具装箱 (斜率优化DP)
- BZOJ_P1010 [HNOI2008]玩具装箱toy(斜率优化DP)
- BZOJ 1010-玩具装箱toy(DP+斜率优化)
- BZOJ 1010 [HNOI2008]玩具装箱toy 斜率优化DP
- bzoj1010: [HNOI2008]玩具装箱toy 斜率优化DP
- 【bzoj 1010】[HNOI2008] 玩具装箱toy(斜率优化dp)
- _bzoj1010 [HNOI2008]玩具装箱toy【斜率优化dp】
- BZOJ 1010 [HNOI2008]玩具装箱toy (斜率优化DP)
- BZOJ.1010.[HNOI2008]玩具装箱toy(DP 斜率优化/单调队列 决策单调性)
- bzoj 1010 [HNOI2008]玩具装箱toy(DP的斜率优化)
- BZOJ 1010 玩具装箱 toy (DP斜率优化)
- HNOI2008玩具装箱Toy 斜率优化DP
- 【bzoj1010】玩具装箱toy 斜率优化dp
- 【OI做题记录】【BZOJ】【斜率优化DP】【HNOI2008】玩具装箱
- BZOJ1010 [HNOI2008]玩具装箱toy(斜率优化dp)
- BZOJ 1010: [HNOI2008]玩具装箱toy 斜率优化DP
- bsoj 2430 HNOI2008 玩具装箱(DP+斜率优化)
- [BZOJ1010][HNOI2008]玩具装箱(决策单调性/斜率优化DP)
- BZOJ 1010: [HNOI2008]玩具装箱toy(dp+斜率优化)
- BZOJ1010[HNOI2008]玩具装箱toy——斜率优化DP