hdu3507 Print Article(斜率优化)

题目
题解
此题是很基础的斜率DP的入门题。
题意很清楚,就是输出序列a[n],每连续输出的费用是连续输出的数字和的平方加上常数M
让我们求这个费用的最小值。
设dp[i]表示输出前i个的最小费用,那么有如下的DP方程:
d p [ i ] = m i n { d p [ j ] + ( s u m [ i ] − s u m [ j ] ) 2 + M } ( 0 < j < i ) dp[i]= min \lbrace{ dp[j]+(sum[i]-sum[j])^2 +M \rbrace} (0<j<i) dp[i]=min{dp[j]+(sum[i]sum[j])2+M}(0<j<i)
其中 sum[i]表示数字的前i项和。
相信都能理解上面的方程。
直接求解上面的方程的话复杂度是 O ( n 2 ) O(n^2) O(n2)
对于500000的规模显然是超时的。下面讲解下如何用斜率优化DP使得复杂度降低一维。
我们首先假设在算 dp[i]时, k < j k<j k<j,j点比k点优。
也就是
d p [ j ] + ( s u m [ i ] − s u m [ j ] ) 2 + M < = d p [ k ] + ( s u m [ i ] − s u m [ k ] ) 2 + M ; dp[j]+(sum[i]-sum[j])^2+M <= dp[k]+(sum[i]-sum[k])^2+M; dp[j]+(sum[i]sum[j])2+M<=dp[k]+(sum[i]sum[k])2+M;
所谓j比k优就是DP方程里面的值更小
对上述方程进行整理很容易得到:
( d p [ j ] + s u m 2 [ j ] ) − ( d p [ k ] + s u m 2 [ k ] ) 2 ( s u m [ j ] − s u m [ k ] ) ≤ s u m [ i ] . \frac{(dp[j]+sum^2[j])-(dp[k]+sum^2[k])}{2(sum[j]-sum[k])}≤sum[i]. 2(sum[j]sum[k])(dp[j]+sum2[j])(dp[k]+sum2[k])sum[i].
注意整理中要考虑下正负,涉及到不等号的方向。
左边我们发现如果令: y j = d p [ j ] + s u m 2 [ j ] yj=dp[j]+sum^2[j] yj=dp[j]+sum2[j]
x j = 2 s u m [ j ] xj=2sum[j] xj=2sum[j]
那么就变成了斜率表达式: y j − y k x j − x k ≤ s u m [ i ] ; \frac{yj-yk}{xj-xk}≤sum[i]; xjxkyjyksum[i];
而且不等式右边是递增的。
所以我们可以看出以下两点:我们令 g [ k , j ] = y j − y k x j − x k g[k,j]=\frac{yj-yk}{xj-xk} g[k,j]=xjxkyjyk
第一:如果上面的不等式成立,那就说j比k优,而且随着i的增大上述不等式一定是成立的,也就是对i以后算DP值时,j都比k优。那么k就是可以淘汰的。
第二:如果 k < j < i k<j<i k<j<i而且 g [ k , j ] > g [ j , i ] g[k,j]>g[j,i] g[k,j]>g[j,i]那么 j 是可以淘汰的。
假设 g [ j , i ] ≤ s u m [ i ] g[j,i]≤sum[i] g[j,i]sum[i]就是i比j优,那么j没有存在的价值
相反如果 g [ j , i ] > s u m [ i ] g[j,i]>sum[i] g[j,i]>sum[i]那么同样有 g [ k , j ] > s u m [ i ] g[k,j]>sum[i] g[k,j]>sum[i]那么 k比 j优 那么 j 是可以淘汰的
所以这样相当于在维护一个下凸的图形,斜率在逐渐增大。
通过一个队列来维护。

#include<bits/stdc++.h>
using namespace std;
const int N=500002;
int n,m,dp[N],sum[N],q[N],h,t,i;
#define get(i,j) dp[j]+(sum[i]-sum[j])*(sum[i]-sum[j])+m
#define up(i,j) (dp[i]+sum[i]*sum[i]-dp[j]-sum[j]*sum[j])
#define down(i,j) (sum[i]-sum[j])*2
int main(){
    while (~scanf("%d%d",&n,&m)){
        for (i=1;i<=n;i++) scanf("%d",&sum[i]),sum[i]+=sum[i-1];
        h=0;t=1;q[0]=0;
        for (i=1;i<=n;i++){
            while (h+1<t && up(q[h+1],q[h])<=sum[i]*down(q[h+1],q[h])) h++;
            dp[i]=get(i,q[h]);
            while (h+1<t && up(i,q[t-1])*down(q[t-1],q[t-2])<=up(q[t-1],q[t-2])*down(i,q[t-1])) t--;
            q[t++]=i;
        }
        printf("%d\n",dp[n]);
    }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值