hdu 3507

hdu 3507


原题

题意:

给定非负整数序列 a 长度为n,状态转移方程为 DP[i]=minik=1(DP[k]+(sum[i]sum[k])2+C) ,求DP[n]

思路:

作为斜率优化的例题,在斜率优化的介绍里挺清楚的……

代码:

# include<stdio.h>

const int nMax=500000;

int in[nMax+1], que[nMax], head, tail, n, m;
long long sum[nMax+1], dp[nMax+1];

void SetDP(int i, int k){dp[i]=dp[k]+(sum[i]-sum[k])*(sum[i]-sum[k])+m;}
long long F(int i){return dp[i]+sum[i]*sum[i];}
long long G(int i){return sum[i]<<1;}
long long H(int i){return sum[i];}

int main(){
    sum[0]=dp[0]=0;
    while(~scanf("%d%d", &n, &m)){
        head=tail=0;
        que[tail++]=0;
        for(int i=1; i<=n; i++){
            scanf("%d", in+i);
            sum[i]=sum[i-1]+in[i];
            /*
                循环弹出队首直到队列里只剩一个元素或队首的两个元素间斜率不满足要求
            */
            while(tail>head+1){
                int l=que[head], r=que[head+1];
                if(F(l)-F(r) >= H(i)*(G(l)-G(r))) head++;
                else break;
            }
            //得到DP值
            SetDP(i, que[head]);
            /*
                循环删除队尾直到队列里只剩一个元素或满足队列中连续元素间斜率递增
            */
            while(tail>head+1){
                int l=que[tail-2], m=que[tail-1], r=i;
                if((G(r)-G(m))*(F(m)-F(l)) >= (F(r)-F(m))*(G(m)-G(l))) tail--;
                else break;
            }
            //加入i点
            que[tail++]=i;
        }
        printf("%lld\n", dp[n]);
    }
    return 0;
}

斜率优化介绍里的第一种方法

//和上面几乎一样的代码
# include<stdio.h>

const int nMax=500000;

int in[nMax+1], sta[nMax], sTop, lastP, n, m;
long long sum[nMax+1], dp[nMax+1];

void SetDP(int i, int k){dp[i]=dp[k]+(sum[i]-sum[k])*(sum[i]-sum[k])+m;}
long long F(int i){return dp[i]+sum[i]*sum[i];}
long long G(int i){return sum[i]<<1;}
long long H(int i){return sum[i];}

int main(){
    sum[0]=dp[0]=0;
    while(~scanf("%d%d", &n, &m)){
        sTop=0;
        lastP=0;
        sta[sTop++]=0;
        for(int i=1; i<=n; i++){
            scanf("%d", in+i);
            sum[i]=sum[i-1]+in[i];
            /*
            若上一个点的最优分割点在加入点i-1时被删除出了凸包,则该点的最优分割点必定是点i-1,否则从原最优分割点向后枚举直到斜率不满足要求或到达栈顶
            */
            if(lastP>sTop-2) lastP=sTop-1;
            else while(sTop>lastP+1){
                int l=sta[lastP], r=sta[lastP+1];
                if(F(l)-F(r) >= H(i)*(G(l)-G(r))) lastP++;
                else break;
            }
            SetDP(i, sta[lastP]);
            /*
                循环删除栈顶直到栈里只剩一个元素或满足栈中连续元素间斜率递增
            */
            while(sTop>1){
                int l=sta[sTop-2], m=sta[sTop-1], r=i;
                if((G(r)-G(m))*(F(m)-F(l)) >= (F(r)-F(m))*(G(m)-G(l))) sTop--;
                else break;
            }
            sta[sTop++]=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、付费专栏及课程。

余额充值