HDU 3507 Print Article 斜率dp

题目链接:点击打开链接

题意:

给定n m

下面n个数

dp方程: dp[i] = dp[j] + sum[j+1, i] ^2 +m; ( j < i)

思路:

斜率优化

设 k < j,若从j转移过来更优则满足:

dp[j] + sum[j+1, i]^2 +m <= dp[k] + sum[k+1, i]^2 + m

整理得到

1、

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

注意sum[i]是不断增加的。所以一旦j点比k点更优,那么在i+1, i+2等也满足上面的不等式,即k点可以被放弃。

2、

当加入i点后,则 判断 i-1这点是否需要放弃。

双端队列维护一个斜率严格递增的图形。

这个图形的x轴是给出的n

y轴是斜率的值,即((dp[j]+sum[j]^2) - (dp[k] + sum[k]^2) ) / (2*(sum[j]-sum[k])) 

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
template <class T>
inline bool rd(T &ret) {
    char c; int sgn;
    if(c=getchar(),c==EOF) return 0;
    while(c!='-'&&(c<'0'||c>'9')) c=getchar();
    sgn=(c=='-')?-1:1;
    ret=(c=='-')?0:(c-'0');
    while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
    ret*=sgn;
    return 1;
}
template <class T>
inline void pt(T x) {
    if (x <0) {
        putchar('-');
        x = -x;
    }
    if(x>9) pt(x/10);
    putchar(x%10+'0');
}
using namespace std;
const int N = 505000;
int n, m, a[N], sum[N], dp[N];
int q[N*2], tail, head;
int getDp(int i, int j){
    return dp[j] + (sum[i]-sum[j])*(sum[i]-sum[j]) + m;
}
int getUp(int j, int k){
    return dp[j]+sum[j]*sum[j] - (dp[k]+sum[k]*sum[k]);
}
int getDown(int j, int k){
    return 2*(sum[j]-sum[k]);
}
int main(){
    while(~scanf("%d %d", &n, &m)){
        sum[0] = 0;
        for(int i = 1; i <= n; i++)
        {
            rd(a[i]);
            sum[i] = sum[i-1]+a[i];
        }
        dp[0] = 0;
        head = tail = 0;
        q[tail++] = 0;
        for(int i = 1; i <= n; i++)
        {
            while(head+1 < tail && getUp(q[head+1], q[head])<=sum[i]*getDown(q[head+1],q[head]))
                head++;
            dp[i] = getDp(i, q[head]);
            while(head+1 < tail && getUp(i, q[tail-1])*getDown(q[tail-1], q[tail-2]) <= getUp(q[tail-1], q[tail-2])*getDown(i, q[tail-1]))
                tail--;
            q[tail++] = i;
        }
        cout<<dp[n]<<endl;
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值