hdu3507(斜率dp)

链接:点击打开链接

题意:给出n个数,要求按顺序全部取出,每次取出一段所花费的费用为取出一段数的和的平方加m,问最小费用是多少

代码1:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int a[500005],q[500005],dp[500005];
int in(int k1,int k2,int ai){
    int x1,x2;
    x1=dp[k2]-dp[k1]+a[k2]*a[k2]-a[k1]*a[k1];
    x2=2*ai*(a[k2]-a[k1]);
    if(x1<=x2)
    return 1;
    return 0;
}
int out(int k1,int k,int k2){
    int x1,x2;
    x1=(dp[k]-dp[k1]+a[k]*a[k]-a[k1]*a[k1])*(a[k2]-a[k]);
    x2=(dp[k2]-dp[k]+a[k2]*a[k2]-a[k]*a[k])*(a[k]-a[k1]);
    if(x1>=x2)
    return 1;
    return 0;
}
int main(){                                     //dp[i]表示输出到第i位的最小费用
    int n,m,i,l,r,k,k1,k2,cas;                  //因此很容易推出dp[i]=min(dp[i],dp[j]+(sum[i]-sum[j])^2+m)
    while(scanf("%d%d",&n,&m)!=EOF){            //但是复杂度为O(n^n),一定会超时,
        for(i=1;i<=n;i++)
        scanf("%d",&a[i]);                      //假设j<k,假设k优于j
        for(i=2;i<=n;i++)                       //dp[k]+(sum[i]-sum[k])^2+m<dp[j]+(sum[i]-sum[j])^2+m
        a[i]+=a[i-1];                           //G[j,k]=((dp[k]+sum[k]^2)-(dp[j]+sum[j]^2))/(2*sum[j]*(sum[k]-sum[j]))<=1时
        memset(dp,0,sizeof(dp));                //假设成立,也就是k优于j,否则j优于k
        l=r=0;                                  //当k1<k<k2时,G[k1,k]>=G[k,k2]时,k没有用处
        q[0]=0;                                 
        for(i=1;i<=n;i++){
        while(r-l>0){
            k1=q[l],k2=q[l+1];
            if(in(k1,k2,a[i]))
            l++;
            else
            break;
        }
        dp[i]=dp[q[l]]+(a[i]-a[q[l]])*(a[i]-a[q[l]])+m;
        while(r-l>0){
            k1=q[r-1],k=q[r],k2=i;
            if(out(k1,k,k2))
            r--;
            else
            break;
        }
        q[++r]=i;
        }
        printf("%d\n",dp[n]);
    }
    return 0;
}

代码2:

#include <math.h>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
long long sum[500005],que[500005];
long long a[500005],q[500005],dp[500005];
long long getx(long long x){
    return 2*sum[x];
}
long long gety(long long x){
    return dp[x]+sum[x]*sum[x];
}
long long cross(long long x1,long long y1,long long x2,long long y2,long long x3,long long y3){
    return (y3-y1)*(x2-x1)-(y2-y1)*(x3-x1);
}
int main(){
    long long i,k,n,m,head,tail;
    while(scanf("%I64d%I64d",&n,&m)!=EOF){
        sum[0]=0;
        for(i=1;i<=n;i++){
            scanf("%I64d",&a[i]);
            sum[i]=sum[i-1]+a[i];
        }
        memset(dp,0,sizeof(dp));
        head=tail=0;
        q[tail++]=0;
        for(i=1;i<=n;i++){
            k=sum[i];
            while((head+1<tail)&&((gety(q[head+1])-gety(q[head]))<=k*(getx(q[head+1])-getx(q[head]))))
            head++;
            dp[i]=gety(q[head])-k*getx(q[head]);
            dp[i]+=sum[i]*sum[i]+m;
            while((head+1<tail)&&cross(getx(q[tail-2]),gety(q[tail-2]),getx(q[tail-1]),gety(q[tail-1]),getx(i),gety(i))<=0ll)
            tail--;
            q[tail++]=i;
        }
        printf("%I64d\n",dp[n]);
    }
    return 0;
}
代码2主要是根据点击打开链接这个链接进行的学习,用图进行结合,很好理解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值