• HDU-3507 Print Article(dp+斜率优化)


    解题思路:

    这道题的意思就是说输出一串数字,可以在不同行, cost 每行数字的平方+m, 求cost的最小值

    这道题咋看一看dp 解就可以了  dp[i] = dp[j] + (sum[i]-sum[j])^2;很容易就能得出dp方程,但是时间复杂度是O(n^2),所以会爆。

    所以需要优化它, 这种优化方法叫斜率优化(用斜率的形式得出谁是优解)

    比如计算i 与 j,k 之间的优解,假设j 比k优

      dp[j] + (sum[i]-sum[j])^2 <= dp[k] + (sum[i] -sum[k])^2;

    移项可得: dp[j]+sum[j]^2-(dp[k]+sum[k]^2)/(2*(sum[j]-sum[k])<sum[i]①

    令f(sum[x]) = dp[x]-sum[x];则有①可得 f(sum[j]) - f(sum[k])/2*(sum[j]-sum[k]) ②

    当②<=sum[i] 是证明j比k优,否则k比j优;

    我们可以借助这个性质,将一些不必要的点给剔除;

    用g[j,k] 表示②那种形式

    1. 如果g[j,k]<=sum[i], j比k优

    2.如果g[j,k]<=g[i,j], 当g[i,j]<=sum[x] 时, i比j优,则j可以不要了,因为对于任意x,i都比j优

    当g[i,j]>sum[x]时,j比i优,k比j优, 此时j也可以剔除,

    此性质用来更新最优值队列,对于2的情况都可以将i代替j存入,此时可能会有j优于i 为什么还要替换它,  当j优于i的时候 还有k的存在,所以i和j的存在都是多余的,但是i代替j是g[i,j]<= sum[x] 时需要做的步骤, 所以直接保存,不需要再开分支;

    //代码阶段分析

    需要队列中的最优值的时候,因为每次插入当g[i,j]<= sum[x] 的情况 我们无法识别i和k谁最优,所以要再比较一下

    由于插入队列的条件是getUp(que[tail-1],i)*getDown(que[tail-2],que[tail-1])<=getUp(que[tail-2],que[tail-1])*getDown(que[tail-1],i)

    所以对于队列有存在 g[que[mid],i]>g[que[mid-1],que[mid]](这是退出循环的条件,此时i相当于que[mid+1])

    可以分成2种情况①当g[que[mid],i] >sum[i] 时, que[mid-1]比que[mid]优,que[mid]比i优,则在队列中que[mid-1] 是最优的 不明白的自己再演算下

        ②当g[que[mid-1],que[mid]]<=sum[i] , que[mid]比que[mid-1]优,i比que[mid]优,则que[mid]与i 还需要再比较,直到到队列尾或者①的情况

    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstdio>
    using namespace std;
    
    int dp[500100],sum[500100];
    int que[500100];
    int n,m;
    
    int getDp(int i,int j){
        return dp[j] + m + (sum[i]-sum[j])*(sum[i] - sum[j]);
    }
    
    int getUp(int k,int j){
        return dp[j] + sum[j]*sum[j] - (dp[k] + sum[k]*sum[k]);
    }
    
    int getDown(int k,int j){
        return 2*(sum[j] - sum[k]);
    }
    
    int main(){
        int temp,head,tail;
        
        while(scanf("%d%d",&n,&m)!=EOF){
            sum[0] = 0;
            for(int i=1;i<=n;i++){
                scanf("%d",&temp);
                sum[i] = sum[i-1]+temp;
            }
            
            //for(int i=1;i<=n;i++) cout<<sum[i]<<endl;
            
            head = tail = 0;
            que[tail++] = 0;
            
            for(int i=1;i<=n;i++){
                
                while(head+1<tail&&getUp(que[head],que[head+1])<=sum[i]*getDown(que[head],que[head+1]))
                    head++;
                dp[i] = getDp(i,que[head]);
                
                while(head+1<tail&&getUp(que[tail-1],i)*getDown(que[tail-2],que[tail-1])<=getUp(que[tail-2],que[tail-1])*getDown(que[tail-1],i))
                    tail--;
                que[tail++] = i;        
            }
            printf("%d
    ",dp[n]);
        }
        
        
        return 0;
    } 
  • 相关阅读:
    数组和对象常用方法汇总
    基于vue的悬浮碰撞窗口(用于打广告的)组件
    时间的基本处理
    防抖动和节流阀
    A. 配置xftp和xshell来远程管理Linux服务器
    课堂练习-找水王
    评价软件
    构建之法阅读笔记02
    学习进度条博客11
    用户场景
  • 原文地址:https://www.cnblogs.com/yuanshixingdan/p/5505928.html
Copyright © 2020-2023  润新知