[JZOJ P1329] [DP]玩具装箱
找了好几个大神的博客,然而一堆堆的公式看都看不懂,最后找到了hzwer学长的,第一次看到斜率优化,大概是假设两个k,j决策且k优于j,化成一堆公式,左边跟k,j有关,右边跟i有关。
dp公式很好想, dp[i]=min(dp[j]+(i-j-1-l+sum[i]-sum[j])^2);(j<i)
dp[i]表示前i个玩具的最优费用,dp[i]的值就取决于 dp[j]+从j+1到i玩具的费用 i-j-1 就可以得出。
设f[i]=sum[i]+i, c=1+l;
dp[i]=min(dp[j]++(f[i]-f[j]-c)^2);
证明决策单调性:
假设在状态i处有k和j两种决策,且k优于j
则 dp[k]+(f[i]-f[k]-c)^2<=dp[j]+(f[i]-f[j]-c)^2
在状态i后的状态t也一定是k决策优于j决策
dp[k]+(f[t]-f[k]-c)^2<=dp[j]+(f[t]-f[j]-c)^2
f[t]=sum[t]+t=sum[i]+sum[t]-sum[i]+i+t-i;
设 v=sum[t]-sum[i]+t-i;
f[t]=f[i]+v;
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]
求斜率方程:
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]
当 队首+1优于队首时 ->SLOP(队首+1=k,队首=j) 踢出队首
这酱紫队首为最优,求出dp[i]
把i加入决策中时 当 SLOP(i=k,队尾=j)<SLOP(队尾=k,队尾-1=j) 踢出队尾
把决策i加入队尾。
重要的一点是long long 因为要平方谁也不知道平方后的数有多可怕。不平方只过了样例没过一组数据我不想说话。
#include<iostream> #include<cstring> #include<algorithm> #include<cstdio> using namespace std; long long n,l,cc[50005],dp[50005],q[50005],f[50005],c; int head=1,tail=0; double solve(int j,int k) { return(dp[k]-dp[j]+(f[k]+c)*(f[k]+c)-(f[j]+c)*(f[j]+c))/(2.0*(f[k]-f[j])); } int main(){ cin>>n>>l; for(int i=1;i<=n;i++) cin>>cc[i]; for(int i=1;i<=n;i++) f[i]=f[i-1]+cc[i]; for(int i=1;i<=n;i++) f[i]+=i; c=1+l; q[++tail]=0; for(int i=1;i<=n;i++) { while(head<tail&&solve(q[head],q[head+1])<=f[i]) head++; int t=q[head]; dp[i]=dp[t]+(f[i]-f[t]-c)*(f[i]-f[t]-c); while(head<tail&&solve(q[tail],i)<solve(q[tail-1],q[tail])) tail--; q[++tail]=i; } cout<<dp[n]; return 0; }