HDU - 3507 Print Article 斜率dp

题意
打印一篇长度为n的文章,求最小代价
其中代价的计算公式为:一行元素权值加和的平方加上换行的代价,即下图公式:
这里写图片描述

思路
很容易想到用dp
状态转移也好写
dp[i]=min(dp[j]+(sum[i]-sum[j])^2+m);
然后随便的算一下复杂度 O(n^2)
再看一下数据范围 (0 ≤ n ≤ 500000, 0 ≤ M ≤ 1000)
GG……..
所幸我们可以优化,
dp[i]=min(dp[j]+(sum[i]-sum[j])^2+m);
dp[i]=min(dp[k]+(sum[i]-sum[k])^2+m);
我们假设 j比k优秀

化简可得:

然后令
然后令
化简为

一不小心发现满足斜率优化dp

然后模板走一波 …

不知道斜率dp? 看这里

然后分析一下:

不等式右边是递增的。
令g[k,j]=(yj-yk)/(xj-xk)
可以看出以下两点:
1.如果上面的不等式成立,那就说j比k优,而且随着i的增大上述不等式一定是成立的,也就是对i以后算dp值时,j都比k优。那么k就是可以淘汰的。
2.如果 而且 g[k,j]>g[j,i] 那么 j 是可以淘汰的。
假设 就是i比j优,那么j没有存在的价值
相反如果 g[j,i]>sum[i] 那么同样有 g[k,j]>sum[i] 那么 k比 j优 那么 j 是可以淘汰的

所以这样相当于在维护一个下凸的图形,斜率在逐渐增大。

然后用单调队列就好了

参考代码

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;

long long an[500500];
long long sum[500500];
long long n,m;
long long dp[500500];
long long q[500500];
long long front=1,rear=0;
long long zi(long long a,long long b){
    long long ans=dp[a]-dp[b]+ (sum[a]+sum[b])*(sum[a]-sum[b]);
    return ans;
}
long long mu(long long a,long long b){
    return 2*(sum[a]-sum[b]);
}
void calcul(long long i){
    while(front+1<rear&&zi(q[front+1],q[front])<=sum[i]*mu(q[front+1],q[front])){ //单调队列的出队 
        front++;
    }
    dp[i]=dp[q[front]]+(sum[i]-sum[q[front]])*(sum[i]-sum[q[front]])+m;//状态转移方程 
    //维护单调队列(斜率递增) 
    while(front+1<rear&& zi(q[rear-1],q[rear-2])*mu(i,q[rear-1])>=zi(i,q[rear-1])*mu(q[rear-1],q[rear-2])){
        rear--;
    }
    q[rear++]=i;
}


int main(){
//  freopen("in.txt","r",stdin);
//  freopen("out.txt","w",stdout);
    while(~scanf("%lld%lld",&n,&m)){
        front=1,rear=1;
        sum[0]=0;
        for(int i=1;i<=n;i++){
            scanf("%lld",&an[i]);
            sum[i]=sum[i-1]+an[i];
        }
        q[rear++]=0;
        dp[0]=0;
        for(long long i=1;i<=n;i++){
            calcul(i);
        }
        printf("%lld\n",dp[n]);
    }
    return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值