【斜率优化】玩具装箱

题意:

有n 个玩具需要装箱,每个玩具的长度为c[i],规定在装箱的时候,必须严格按照给出的顺序进行,并且同一个箱子中任意两个玩具之间必须且只能间隔一个单位长度,换句话说,如果要在一个箱子中装编号为i~j 的玩具,则箱子的长度必须且只能是l=j-i+sigma[c[k]],规定每一个长度为 l 的箱子的费用是P = (l - L)2,其中 L 是给定的一个常数。现在要求你使用最少的代价将所有玩具装箱,箱子的个数无关紧要.

思路:

dp[i]=min(dp[j]+(sum[i]-sum[j]+i-j-1-L)^2) (j<i)

令f[i]=sum[i]+i,c=1+l

则dp[i]=min(dp[j]+(f[i]-f[j]-c)^2)

1.证明决策单调性

设在状态i处的k决策优与j决策,即

dp[k]+(f[i]-f[k]-c)^2<=dp[j]+(f[i]-dp[j]-c)^2

对于后的某状态t,dp[t]=dp[i]+v;

要证明dp[k]+(f[t]-f[k]-c)^2<=dp[j]+(f[t]-f[j]-c)^2

只要证

dp[k]+(f[i]+v-f[k]-c)^2<=dp[j]+(f[i]+v-f[j]-c)^2

只要证

dp[k]+(f[i]-f[k]-c)^2+2*v*(f[i]-f[k]-c)+v^2<=dp[j]+(f[i]-f[j]-c)^2+2*v*(f[i]-f[j]-c)+v^2

只要证

2*v*(f[i]-f[k]-c)<=2*v*(f[i]-f[j]-c)

即f[k]>=f[j](显然)

证明完毕

2.求斜率方程

因为dp[k]+(f[i]-f[k]-c)^2<=dp[j]+(f[i]-f[j]-c)^2

展开

dp[k]+f[i]^2-2*f[i]*(f[k]+c)+(f[k]+c)^2<=dp[j]+f[i]^2-2*f[i]*(f[j]+c)+(f[j]+c)^2

dp[k]-2*f[i]*(f[k]+c)+(f[k]+c)^2<=dp[j]-2*f[i]*(f[j]+c)+(f[j]+c)^2

即(dp[k]+(f[k]+c)^2-dp[j]-(f[j]+c)^2)/2*(f[k]-f[j])<=f[i]

f[i]是单调递增的,我们使用队列维护一个下凸壳,每次取出队头作为决策

加入决策i时,令队尾为q[r],前一个为q[r-1]

满足斜率(q[r],i)<斜率(q[r-1],q[r])时,显然队尾是无效的,将其弹出(转自

代码:

 1 #include<map>
 2 #include<set>
 3 #include<ctime>
 4 #include<cstdio>
 5 #include<cstring>
 6 #include<vector>
 7 #include<cstdlib>
 8 #include<iostream>
 9 #include<algorithm>
10 #define inf 1000000000
11 #define ll long long
12 using namespace std;
13 ll read()
14 {
15     ll x=0,f=1;char ch=getchar();
16     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
17     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
18     return x*f;
19 }
20 int n,L,l,r;
21 int c[50005],q[50005];
22 ll s[50005],f[50005],C;
23 double slop(int j,int k)
24 {
25     return (f[k]-f[j]+(s[k]+C)*(s[k]+C)-(s[j]+C)*(s[j]+C))/(2.0*(s[k]-s[j]));
26 }
27 void dp()
28 {
29     l=1;r=0;q[++r]=0;
30     for(int i=1;i<=n;i++)
31     {
32         while(l<r&&slop(q[l],q[l+1])<=s[i])l++;
33         int t=q[l];
34         f[i]=f[t]+(s[i]-s[t]-C)*(s[i]-s[t]-C);
35         while(l<r&&slop(q[r],i)<slop(q[r-1],q[r]))r--;
36         q[++r]=i;
37     }
38 }
39 int main()
40 {
41     n=read();L=read();C=L+1;
42     for(int i=1;i<=n;i++)c[i]=read();
43     for(int i=1;i<=n;i++)s[i]=s[i-1]+c[i];
44     for(int i=1;i<=n;i++)s[i]+=i;
45     dp();
46     printf("%lld\n",f[n]);
47     return 0;
48 }
View Code

 

转载于:https://www.cnblogs.com/Rojo/p/4692553.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值