斜率优化dp+洛谷P3195玩具装箱

斜率优化dp是用来处理dp方程形如:

f[i]=min{f[j]}+a[i]的问题的算法

 将该形式的dp方程等价为形如y=kx+b的方程,然后可以表示出不同j时该方程的斜率,通过比较新加入j所对应的斜率和其他线段的斜率,利用单调队列或二分维护极值,最后dp出答案,该算法可以极大的优化运行时间。

 

洛谷P3195玩具装箱

很容易可以得到O(n2)暴力dp:dp[i]=0j<imin{dp[j]+(sum[i]sum[j]+ij1L)2} ==> dp[i]=0j<imin{dp[j]+(f[i]f[j]1L)2}(sum为前缀和,f[i]为sum[i]+i)

这时设j1,j2且j2优于j1,于是有:

dp[j1]+(f[i]f[j11]1L)2dp[j2]+(f[i]f[j2]1L)2

拆开可得

dp[j1]+f[i]22f[i](f[j1]+1+L)+(f[j1]+L+1)2dp[j2]+f[i]22f[i](f[j2]+1+L)+(f[j2]+L+1)2

移项

 

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,L,a[500010],sum[500010],g[500010],f[500010],q[500010],head,tail;
int Read()
{
    int f=0;char ch;ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')f=f*10+(ch-'0'),ch=getchar();
    return f;
}
double check(int x,int y)
{
    //return ((double)(f[x]+g[x]-f[y]-g[y]))/((double)(sum[x]-sum[y]));
    return (double)((f[x]+g[x]-f[y]-g[y])/(sum[x]-sum[y]));
}
signed main()
{
    n=Read(),L=Read();
    g[0]=(L+1)*(L+1);
    for(int i=1;i<=n;i++)
    sum[i]=Read(),sum[i]+=sum[i-1];
    for(int i=1;i<=n;i++)sum[i]+=i,g[i]=(sum[i]+L+1)*(sum[i]+L+1);
    for(int i=1;i<=n;i++)
    {
        while(head<tail&&check(q[head],q[head+1])<=2*sum[i])head++;
        f[i]=f[q[head]]+(sum[i]-sum[q[head]]-L-1)*(sum[i]-sum[q[head]]-L-1);
        while(head<tail&&check(q[tail],i)<check(q[tail-1],q[tail]))tail--;
        q[++tail]=i;
    }
    cout<<f[n];
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/57412626-996842/p/11650803.html
今日推荐