斜率优化dp练习题

此处只有由dp式子推导斜率优化

T[i]为T前缀和,C[i]为C前缀和

f[i]=min(f[j]+S*(C[n]-C[j])+T[i]*(C[i]-C[j])) (0<=j<i)

f[i]=min(f[j]-(S+T[i])*C[j])+S*C[n]+T[i]*C[i]

f[j]=(S+T[i])*C[j]+f[i]-S*C[n]-T[i]*C[i]

坐标(C[j],f[j])

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define ll long long
using namespace std;
const int N=3e5+1;
ll T[N],C[N],S,f[N];
int n,l,r,q[N];
int main()
{
    scanf("%d%lld",&n,&S);
    for(int i=1;i<=n;i++)
        scanf("%lld%lld",&T[i],&C[i]),T[i]+=T[i-1],C[i]+=C[i-1];
    l=1,r=0;
    for(int i=1;i<=n;i++)
    {
        while(l<r && (f[q[r]]-f[q[r-1]])*(C[i-1]-C[q[r]])>=(f[i-1]-f[q[r]])*(C[q[r]]-C[q[r-1]])) r--;
        q[++r]=i-1;
        while(l<r && f[q[l+1]]-f[q[l]]<=(S+T[i])*(C[q[l+1]]-C[q[l]])) l++;
        f[i]=f[q[l]]-(S+T[i])*C[q[l]]+S*C[n]+T[i]*C[i];
    }
    printf("%lld\n",f[n]);    
    return 0;
}
View Code

当斜率S+T[i]不再单挑递增,原本如同单调队列般的删除策略失效

但当斜率k为S+T[i]时,使凹壳f[i]最优点的本质是:

故使用二分查找

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define ll long long
using namespace std;
const int N=3e5+1;
ll T[N],C[N],S,f[N];
int n,l,r,q[N];
int mylower(ll x)
{
    if(l==r) return q[l];
    int L=l,R=r;
    while(L<R)
    {
        int mid=(L+R)>>1;
        if(f[q[mid+1]]-f[q[mid]]<=x*(C[q[mid+1]]-C[q[mid]])) L=mid+1;
        else R=mid;
    }
    return q[L];
}
int main()
{
    scanf("%d%lld",&n,&S);
    for(int i=1;i<=n;i++)
        scanf("%lld%lld",&T[i],&C[i]),T[i]+=T[i-1],C[i]+=C[i-1];
    l=1,r=0;
    for(int i=1;i<=n;i++)
    {
        while(l<r && (f[q[r]]-f[q[r-1]])*(C[i-1]-C[q[r]])>=(f[i-1]-f[q[r]])*(C[q[r]]-C[q[r-1]])) r--;
        q[++r]=i-1;
        int j=mylower(S+T[i]);
        f[i]=f[j]-(S+T[i])*C[j]+S*C[n]+T[i]*C[i];
    }
    printf("%lld\n",f[n]);    
    return 0;
}
View Code

A[i]=T[i]-(D[1]+D[2]+...+D[H[i]])

对A从小到大排序

S[i]=A[1]+A[2]+..+A[i]

f[i][j]=min(f[i-1][k]+A[j]*(j-k)-(S[j]-S[k]))   (0<=k<j)

f[i][j]=min(f[i-1][k]-A[j]*k+S[k])+A[j]*j-S[j]

f[i-1][k]+S[k]=A[j]*k+f[i][j]+S[j]-A[i]*j;

坐标(k,f[i-1][k]+S[k])

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define ll long long
using namespace std;
const int N=1e5+10,M=101;
ll f[M][N],a[N],h[N],t[N],d[N],s[N],pre[N];
int n,m,p,l,r,q[N];
int main()
{
    scanf("%d%d%d",&n,&m,&p);
    for(int i=2;i<=n;i++) scanf("%lld",&d[i]),d[i]+=d[i-1];
    for(int i=1;i<=m;i++) scanf("%lld%lld",&h[i],&t[i]),a[i]=t[i]-d[h[i]];
    sort(a+1,a+1+m);
    for(int i=1;i<=m;i++) s[i]=s[i-1]+a[i],f[1][i]=a[i]*i-s[i],pre[i]=f[1][i]+s[i];
    for(int i=2;l=1,r=0,i<=p;i++)
        for(int j=1;j<=m;j++)
        {
            pre[j-1]=f[i-1][j-1]+s[j-1];
            while(l<r && (pre[q[r]]-pre[q[r-1]])*(j-1-q[r])>=(pre[j-1]-pre[q[r]])*(q[r]-q[r-1])) r--;
            q[++r]=j-1;
            while(l<r && pre[q[l+1]]-pre[q[l]]<=a[j]*(q[l+1]-q[l])) l++;
            f[i][j]=f[i-1][q[l]]+a[j]*(j-q[l])-(s[j]-s[q[l]]);
        }
    printf("%lld\n",f[p][m]);
    return 0;
}
View Code

S[i]=C[1]+C[2]+..+C[i]

f[i]=min(f[j]+(i-j-1+S[i]-S[j]-L)2)  (0<=j<i)

化简: S[i]+=i,L++;

f[i]=min(f[j]+(S[i]-S[j]-L)2)  (0<=j<i)

f[i]=min(f[j]+S[j]2-2*(S[i]-L)*S[j])+(S[i]-L)2

f[j]+S[j]2=2*(S[i]-L)*S[j]+f[i]-(S[i]-L)2

坐标(S[j],f[j]+S[j]2)

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define ll long long
using namespace std;
const int N=5e4+10;
int n,l=1,r,q[N];
ll L,c[N],s[N],pre[N],f[N];
ll pf(ll x)
{
    return x*x;
}
int main()
{
    scanf("%d%lld",&n,&L);
    L++;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&c[i]);
        c[i]+=c[i-1];
        s[i]=c[i]+i;
    }
    for(int i=1;i<=n;i++)
    {
        pre[i-1]=f[i-1]+pf(s[i-1]);
        while(l<r && (pre[q[r]]-pre[q[r-1]])*(s[i-1]-s[q[r]])>=(pre[i-1]-pre[q[r]])*(s[q[r]]-s[q[r-1]])) r--;
        q[++r]=i-1;
        while(l<r && pre[q[l+1]]-pre[q[l]]<=(s[q[l+1]]-s[q[l]])*2*(s[i]-L)) l++;
        f[i]=f[q[l]]+pf(s[i]-s[q[l]]-L);
    }
    printf("%lld\n",f[n]);
    return 0;
}
View Code

SP[i]=P[1]+P[2]+..+P[i]

SX[i]=X[1]+X[2]+..+X[i]

SXP[i]=X[1]*P[1]+X[2]*P[2]+..+X[i]*P[i]

f[i]=min(f[j]+C[i]+X[i]*(SP[i]-SP[j])-SXP[i]+SXP[j]) (0<=j<i)

f[i]=min(f[j]-X[i]*SP[j]+SXP[j])-SXP[i]+X[i]*SP[i]

f[j]+SXP[j]=X[i]*SP[j]+~~

坐标(SP[j],f[j]+SXP[j])

#include <cstdio>
#include <cstdlib>
#define ll long long
using namespace std;
const int N=1e6+10;
ll x[N],p[N],sp[N],f[N],sx[N],sxp[N],pre[N],c[N];
int n,l=1,r,q[N];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld%lld",&x[i],&p[i],&c[i]);
        sx[i]=sx[i-1]+x[i],sp[i]=sp[i-1]+p[i];
        sxp[i]=sxp[i-1]+x[i]*p[i];
    }
    for(int i=1;i<=n;i++)
    {
        pre[i-1]=f[i-1]+sxp[i-1];
        while(l<r && (pre[q[r]]-pre[q[r-1]])*(sp[i-1]-sp[q[r]])>=(pre[i-1]-pre[q[r]])*(sp[q[r]]-sp[q[r-1]])) r--;
        q[++r]=i-1;
        while(l<r && pre[q[l+1]]-pre[q[l]]<=x[i]*(sp[q[l+1]]-sp[q[l]])) l++;
        f[i]=f[q[l]]+c[i]+x[i]*(sp[i]-sp[q[l]])-sxp[i]+sxp[q[l]];
    }
    printf("%lld\n",f[n]);
    return 0;
}
View Code

S[i]=X[1]+X[2]+..+X[i]

f[i]=max(f[j]+a*(S[i]-S[j])2+b*(S[i]-S[j])+c) (0<=j<i)

f[i]=max(f[j]-2a*S[i]*S[j]+a*S[j]2-b*S[j])+c+b*S[i]+a*S[i]2

f[j]+a*S[j]2-b*S[j]=2a*S[i]*S[j]+f[i]-c-b*S[i]-a*S[i]2

坐标(S[j],f[j]+a*S[j]2-b*S[j])

注意这里是max,所以要维护一个凸壳,另外决策时也有些不一样(画个图)

#include <cstdio>
#include <cstdlib>
#define ll long long
using namespace std;
const int N=1e6+10;
ll a,b,c,x[N],pre[N],f[N];
int n,l=1,r,q[N];
ll pf(ll x)
{
    return x*x;
}
//f[j]+a*S[j]2-b*S[j]=2a*S[i]*S[j]+f[i]-c-b*S[i]-a*S[i]2
int main()
{
    scanf("%d%lld%lld%lld",&n,&a,&b,&c);
    for(int i=1;i<=n;i++) scanf("%lld",&x[i]),x[i]+=x[i-1];
    for(int i=1;i<=n;i++)
    {
        pre[i-1]=f[i-1]+a*pf(x[i-1])-b*x[i-1];
        while(l<r && (pre[q[r]]-pre[q[r-1]])*(x[i-1]-x[q[r]])<=(pre[i-1]-pre[q[r]])*(x[q[r]]-x[q[r-1]])) r--;
        q[++r]=i-1;
        while(l<r && pre[q[l+1]]-pre[q[l]]>=2*a*x[i]*(x[q[l+1]]-x[q[l]])) l++;
        f[i]=f[q[l]]+a*pf(x[i]-x[q[l]])+b*(x[i]-x[q[l]])+c;
    }
    printf("%lld\n",f[n]);
    return 0;
}
View Code

斜率优化套路都差不多,但要理解优化原理,以便针对不同情况(如max)

可以考虑斜率优化的式子大概是这个结构:

f([...])[i]=max/min(f[j]+只与j有关的式子+与i,j都有关的式子)+只与i有关的式子

 

猜你喜欢

转载自www.cnblogs.com/hsez-cyx/p/12307395.html