• HDU3507 Print Article(经典斜率优化dp)


    一道很老的斜率优化dp

    斜率优化看上去很难,其实是有技巧的 。

    对于dp题目,如果你想优化他,一定要先列出朴素的表达式并观察性质

    对于本题我们可以发现,如果要更新dp[i],我们就要从前面找到dp[j]+(s[i]-s[j])^2+m的最小值,其中s是前缀和

    我们就可以猜测,一定有很多不可能转移的内容,我们应该如何删除它从而降低复杂度。

    那么我们假设k<j,当i出现之后,k就不可能作为答案,那么这些k在i处满足的性质就是

    dp[j]+(s[i]-s[j])^2+m<=dp[k]+(s[i]-s[k])^2+m

    对这个式子进行化简移向,我们就可以得到一个式子

    dp[j]+s[j]^2-(dp[k]+s[k]^2)/(2*(s[j]-s[k]))<=s[i]

    为什么我们会想到这样移向?因为常见的优化就只有几种,例如四边形优化,单调队列优化,斜率优化,这个初始的式子中有两个变量,还是成对出现的

    我们就可以猜测可以表达成斜率,当然这也是一种经验。

    所以满足这个情况的k是不满足的当前i的

    其次,我们注意到随着i的增长,s[i]是越来越大的,因此这个k永远都不会用到。

    这是从头部删除,我们知道,斜率优化还有从尾部删除的,这些都有一种特定的公式,就是倒数第二条斜率比倒数第一条要大,也叫做凸包优化。

    我们肯定想不出这样的技巧,但是前人的经验告诉我们这个就是斜率优化的凸包优化的技巧,所以我们应当记住这个结论,并对每个题分析证明这个结论在该题也成立

    下面我们来分析,我们不妨记上面的公式为g[k,j]

    对于新加入的i,如果g[k,j]>g[j,i]我们需要证明j永远用不到

    1.假设g[j,i]<=s[i],根据我们上面推出来的公式,j可以删除

    2.g[j,i]>=s[i],那么g[k,j]>s[i]j依旧不会成为答案,因为k比j更适合

    所以我们成功证明

    下面就是套斜率板子就行。

    本题题解借鉴了kuangbin的题解(思路都是kuangbin老师的),我写下为了自我学习,也为了给刚学习斜率优化的同学一点斜率的技巧。

    #include<cstdio>
    #include<iostream>
    #include<string>
    #include<queue>
    using namespace std;
    const int N=500010;
    int dp[N];
    int q[N];
    int sum[N];
    int hh,tt,n,m;
    int cal(int i,int j){
        return dp[j]+m+(sum[i]-sum[j])*(sum[i]-sum[j]);
    }
    int check(int j,int k){
        return dp[j]+sum[j]*sum[j]-(dp[k]+sum[k]*sum[k]);
    }
    int get(int j,int  k){
        return 2*(sum[j]-sum[k]);
    }
    int main(){
    
        while(cin>>n>>m){
            for(int i=1;i<=n;i++)
               scanf("%d",&sum[i]);
            sum[0]=dp[0]=0;
            for(int i=1;i<=n;i++)
               sum[i]+=sum[i-1];
            hh=tt=0;
            q[0]=0;
            for(int i=1;i<=n;i++){
                while(hh+1<=tt&&check(q[hh+1],q[hh])<=sum[i]*get(q[hh+1],q[hh]))
                   hh++;
                dp[i]=cal(i,q[hh]);
                while(hh+1<=tt&&check(i,q[tt])*get(q[tt],q[tt-1])<=check(q[tt],q[tt-1])*get(i,q[tt]))
                        tt--;
                q[++tt]=i;
            }
            printf("%d
    ",dp[n]);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    出现身份验证错误 要求的函数不受支持,又找不到加密Oracle修正(亲测有效)
    .NET 同步钉钉接口的排班,和审批,并用审批回改排班,上班还是休息,请假或加班上午下午
    C# .NET 遍历Json 形成键值对 取节点值key value
    .net日期类与UNIX时间戳的相互转换,长数字
    PB里取datawindow类型的窗口名称
    js,JQ设置div或标签控件鼠标不可点击
    sql在所有存储过程中查询包含某字符串的执行语句
    QTableView之一:基本使用
    LeetCode 1317. 将整数转换为两个无零整数的和
    C++如何限制类对象只能静态分配或者只能只能动态分配
  • 原文地址:https://www.cnblogs.com/ctyakwf/p/12266250.html
Copyright © 2020-2023  润新知