仓库建设-斜率优化DP

ZJOI2007仓库建设

讲一个比较呆一点的做法:

设f[i][1]表示后i个点且i点建仓库的最优解,f[i][0]表示后i个点且i点不建仓库的最优解。

那么显然可以从后往前DP:

f[i][0]=mini<j<=n{f[j][1]+∑i<=k<j(X[j]-X[k])*P[j]};

f[i][1]=mini<j<=n{f[i+1][0],f[i+1][1]}+C[i];

令S[j]=∑i<=k<j(X[j]-X[k])*P[k]。

则S[j]=∑i<=k<j(X[j]*P[k]-X[k]*P[k])=∑i<=k<jX[j]*P[k]-∑i<=k<jX[k]*P[k]=X[j]*∑i<=k<jP[k]-∑i<=k<jX[k]*P[k]。

不妨令A[x]=X[x]*P[x]。

那么S[j]可以用后缀和表示为:S=X[j]*(sumP[i]-sumP[j])-(sumA[i]-sumA[j])。         

所以f[i][0]=min{f[j][1]+X[j]*(sufP[i]-sufP[j])-(sufA[i]-sufA[j])} 。

把式子化成一次函数可得:

f[j]-X[j]*sufP[j]+sufA[j]=-sufP[i]*X[j]+sufA[i]+f[i]

从后往前维护一个斜率单调递减的下凸壳即可。

 

需要注意的是,每次应先转移好fi

因为y中含有f[i],没转移好f[i]会影响i入队时去尾操作。

#include<bits/stdc++.h>
#define RG register
#define IL inline
#define DB double 
#define int long long
using namespace std;

IL int gi() {
    RG int x=0,w=0; char ch=0;
    while (ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return w?-x:x;
}

const int N=1e6+10;

int l,r,q[N];
int n,P[N],X[N],C[N],A[N],sufA[N],sufP[N],f[N][2];

IL DB Y(int x) {return (DB)f[x][1]-X[x]*sufP[x]+sufA[x];}
IL DB slope(int x,int y) {return (Y(x)-Y(y))/(DB)(X[x]-X[y])*1.0;}

signed main ()
{
    RG int i,j;
    n=gi();
    for (i=1;i<=n;++i)
        X[i]=gi(),P[i]=gi(),C[i]=gi(),A[i]=P[i]*X[i];
    for (i=n;i>=1;--i)
        sufP[i]=sufP[i+1]+P[i],sufA[i]=sufA[i+1]+A[i];
    memset(f,0x3f,sizeof(f));
    l=r=1,q[1]=n,f[n][1]=C[n];
    for (i=n-1;i>=1;--i) {
        f[i][1]=min(f[i+1][0],f[i+1][1])+C[i];
        /*for (j=i+1;j<=n;++j)
          f[i][0]=min(f[i][0],f[j][1]+X[j]*(sufP[i]-sufP[j])-(sufA[i]-sufA[j]));
          //f[j][1]-X[j]*sufP[j]+sufA[j]=-sufP[i]*X[j]+sufA[i]+f[i][0];*/
        while (l<r&&slope(q[l],q[l+1])>=(DB)-sufP[i]) ++l;
        f[i][0]=f[q[l]][1]+X[q[l]]*(sufP[i]-sufP[q[l]])-(sufA[i]-sufA[q[l]]);
        while (l<r&&slope(i,q[r])>=slope(q[r],q[r-1])) --r;
        q[++r]=i;
    }
    printf("%lld
",min(f[1][1],f[1][0]));
    return 0;
}
BY BHLLX
原文地址:https://www.cnblogs.com/Bhllx/p/10368232.html