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

时间:2022-02-06 21:12:53

解题思路:

这道题的意思就是说输出一串数字,可以在不同行, 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\n",dp[n]);
    }
    
    
    return 0;
}