zoukankan      html  css  js  c++  java
  • 斜率优化总结

    考虑如下(dp)
    (dp(i)=max/min(A(i)+B(j)+C(i)D(j))) ((j < i))
    其中,A(i),C(i)只与i有关,B(j),D(j)只与j有关。
    括号里有与i,j同时有关的项,导致单调队列优化失效,但是如果这样的项只有一个可以采用斜率优化。方法如下:
    将此dp方程转化为(dp(i)-A(i)=max/min(B(j)-C(i)(-D(j)) ))的形式。
    设b=dp(i)-A(i),y=B(j),k=C(i),x=-D(j)
    (b=y-kx),这时,只要使b最小/最大,就能使结果最小/最大。
    移项,得(y=kx+b),转化为直线的形式。每计算完一个j就可以计算出对应的x,y.
    其实就是把要最优的值设为b,只和j相关的项设为y,同时相关的设为kx。
    还有类似(b=ay-kx)的形式(两项相关),只要转为(b/a=y-(k/a)*x)即可。
    以最大为例(最小类似),现在就是要在所有的(x,y)中找出一对使得b最大。
    把(x,y)表示为坐标系中点的形式,就是找一个点使得斜率为k的直线经过这个点时与y轴的交点的y坐标尽量大。
    可以发现把斜率为k的直线放在无限高处,再向下平移,接触到的第一个点就是结果。
    如图:

    因为要求最大值,所以只有上凸包上的点有可能成为最大值,其他点可以删除。

    这时,红色箭头即为b,绿色箭头即为最优的(x,y)。dp(i)就是b+A(i)。
    如果x单调,k也单调,就可以用队列或栈来维护凸包。(时间复杂度(O(N))
    如果k不单调,可以在凸包上二分,找到第一个斜率小于k的位置,并计算解。(时间复杂度(O(NlogN))
    如果x不单调,可以用splay维护凸包,或利用CDQ分治,在每层将x,k排序,使x,k递增。(时间复杂度(O(NlogN))
    例题:bzoj1911[Apio2010] 特别行动队
    设 f[i] 表示将前 i 个分组的最优值,则有转移方程式:
    (f[i]=max( f[j]+a×(C[i]-C[j])^2+b×(C[i]-C[j])+c ))
    经过化简得到:
    (f[i]=max( f[j]+a×C[j]^2-b×C[j]-2×a×C[i]×C[j]+a×C[i]^2+b×C[i]+c ))
    (A(i)=a×C[i]^2+b×C[i]+c)
    (B(j)=f[j]+a×C[j]^2-b×C[j])
    (C(i)=2×a×C[i])
    (D(j)=-C[j])
    就可以进行斜率优化了。且x单调递增,k单调递减,可以使用队列维护凸包。
    代码:

    #include <stdio.h>
    #define ll long long
    ll dp[1000010];
    int sz[1000010],S[1000010];
    ll X[1000010],Y[1000010],he=0,ta=0;
    int main()
    {
        int n,a,b,c;
        scanf("%d%d%d%d",&n,&a,&b,&c);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&sz[i]);
            S[i]=sz[i]+S[i-1];
        }
        for(int i=0;i<=n;i++)
        {
            if(i==0)
                dp[i]=0;
            else
            {
                int k=2*a*S[i];
                while(he+1<ta&&Y[he]-k*X[he]<Y[he+1]-k*X[he+1])//从队首删除点
                    he+=1;
                dp[i]=Y[he]-k*X[he]+(ll)a*S[i]*S[i]+(ll)b*S[i]+c;//计算dp值
            }
            ll x=S[i],y=dp[i]+(ll)a*S[i]*S[i]-(ll)b*S[i];//计算x,y值
            while(he+1<ta&&double(y-Y[ta-1])/(x-X[ta-1])>double(Y[ta-1]-Y[ta-2])/(X[ta-1]-X[ta-2]))//入队
                ta-=1;
            X[ta]=x,Y[ta]=y;
            ta+=1;
        }
        printf("%lld",dp[n]);
        return 0;
    }
    
  • 相关阅读:
    虚树学习笔记
    CF487E Tourists
    [HNOI/AHOI2018]毒瘤
    [HEOI2014]大工程
    hive初始化元数据报错
    layui简单的两个页面
    springboot配置swagger信息入门2
    spark连接hive出现错误,javax.jdo.JDODataStoreException: Required table missing : "`DBS`" in Catalog "" Schema ""
    springboot整合shiro关于任务入门3
    Flink部署Standalone模式
  • 原文地址:https://www.cnblogs.com/lnzwz/p/11246226.html
Copyright © 2011-2022 走看看