[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;
}
嗯?你又来看code?

 

转载于:https://www.cnblogs.com/Kaike/p/6337315.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值