zoukankan      html  css  js  c++  java
  • 斜率优化略解

    斜率优化在dp中是比较常见的优化方法

    斜率优化算法最重要的一步,就是要将状态转移方程改写为y=kx+b的形式。而这一算法主要的难点就在于,对于一个复杂的状态转移方程,我们将哪些项将作为y,哪些项最为k和x,哪些项作为b。

    我们以Luogu P3195 [HNOI2008]玩具装箱TOY为例来说斜率优化

    在本题中,设前缀和为sum[i],由题意易得dp方程:

    [dp[i]=Min(dp[j]+(sum[i]+i-sum[j]-j-L-1)^2) (j<i) ]

    这个方程明显是(O(N^2))的,过不了此题

    (a[i]=sum[i]+i)(b[i]=sum[i]+i+L+1)(这一步是为了简化计算)

    则(先忽略Min):

    $$dp[i]=dp[j]+(a[i]-b[j])^2$$

    展开得:

    $$dp[i]=dp[j]+a[i]^2-2⋅a[i]⋅b[j]+b[j]^2$$

    移项得(移的项实际比较套路):

    $$2⋅a[i]⋅b[j]+dp[i]−a[i]^2=dp[j]+b[j]^2$$

    我们把这个方程转化成y=kx+b的形式:

    (dp[j]+b[j]^2)是y,(2⋅a[i])是k(斜率),(b[j])是x,(dp[i]-a[i]^2)是b(截距)

    这个式子珂以变成平面直角坐标系上的一条一次函数

    因为对于每个i来说,a[i]都是确定的

    所以dp[i]的含义珂以转化为:当上述直线过点(P(b[j],dp[j]+b[j]^2))时,直线在y轴的截距加上(a[i]^2)(一个定值)

    而题目即为找这个截距的最小值

    本题中可能为最优的P点组成了一个下凸包(其他题目可能不同,结合题目具体分析)

    显然,凸包中相邻两点斜率是单调递增的

    而目标直线的斜率(2⋅a[i])也是单调递增的

    满足条件的最优(P_j)一定为第一个(slope(P_j,P_{j+1}) > 2 cdot a[i])的点(slope(a,b)表示a,b两点连线的斜率)

    我们珂以考虑用单调队列来维护(slope(P_j,P_{j+1}))

    完整代码:

    #include <bits/stdc++.h>
    #define N 50005
    #define db double
    #define getchar nc 
    using namespace std;
    inline char nc(){
        static char buf[100000],*p1=buf,*p2=buf; 
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; 
    }
    inline int read()
    {
        register int x=0,f=1;register char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
        return x*f;
    }
    inline void write(register long long x)
    {
        if(!x)putchar('0');if(x<0)x=-x,putchar('-');
        static int sta[35];register int tot=0;
        while(x)sta[tot++]=x%10,x/=10;
        while(tot)putchar(sta[--tot]+48);
    }
    int n,L;
    db sum[N],dp[N];
    int head,tail,q[N];
    inline db a(register int i)
    {
        return sum[i]+i;
    }
    inline db b(register int i)
    {
        return a(i)+L+1;
    }
    inline db Y(register int i)
    {
        return dp[i]+b(i)*b(i);
    }
    inline db slope(register int i,register int j)
    {
        return (Y(i)-Y(j))/(b(i)-b(j));
    }
    int main()
    {
        n=read(),L=read();
        head=tail=1;
        for(register int i=1;i<=n;++i)
        {
            sum[i]=(db)read();
            sum[i]+=sum[i-1];
            while(head<tail&&slope(q[head],q[head+1])<a(i)*2)
                ++head;
            dp[i]=dp[q[head]]+(a(i)-b(q[head]))*(a(i)-b(q[head]));
            while(head<tail&&slope(i,q[tail-1])<slope(q[tail-1],q[tail]))
                --tail;
            q[++tail]=i; 
        }
        write((long long)dp[n]);
        return 0;
    }
    

    在这道题中,相邻决策点斜率和每个点的横坐标递增,但在其他题目中还会出现以下情况

    一、相邻两个决策点的斜率不递增

    这种情况下,我们无法保证小于等于当前斜率的决策点j一定也小于下一个状态的斜率。因此不能进行“弹掉队首元素”这一步。但是这不影响我们维护一个下凸壳,因此我们可以在单调队列(下凸壳)内进行二分查找,找到第一个使该决策点和下一个决策点构成的线段斜率>k的决策点,即为当前最优决策。

    二、每个决策点横坐标不递增

    这意味着要在凸壳任意位置动态插入顶点、动态查询,我们珂以使用平衡树来维护凸壳。
  • 相关阅读:
    人月神话--没有银弹软件工程中的根本和次要问题(No Silver Bullet Essence and Accident in Software Engineering)
    人与人之间最难得是合作,组件与组件之间最难得时协作。
    关注软件构架与软件构架师
    软件产品本质是逻辑或者说事概念产品
    为什么需求分析离不开系统逻辑模型
    MySQL中varchar最大长度是多少?【转】
    WCF自定义授权[转自小庄的博客]
    PetShop的系统架构设计[转]
    百练 2734 十进制到八进制 解题报告
    百练 2818 密码
  • 原文地址:https://www.cnblogs.com/yzhang-rp-inf/p/10187703.html
Copyright © 2011-2022 走看看