孤独一生

题目描述

下课了, Polo 来到球场,但他到了之后才发现……被放了飞机……

无事可做的他决心找点乐子,比方说……跳台阶……

球场边有 N 个台阶拍成一行,第 i 个台阶的高度是 Hi(0<Hi<=10^9),第 0个台阶,也就是地面的高度为 0。

Polo 打算把这 N 个台阶分成两个集合 Sa,Sb(可以为空),对于一个台阶集合 S={P1,P2,…P|S|},

其中 P1<P2<…<P|S|,他需要花费 的体力值来完成。

现在他希望两次跳跃所需的总体力值最小,你能帮帮他吗?

题解

考虑一个O(n^2)DP,我们假定 f [ i ] [ j ] f[i][j] f[i][j]表示 A A A集合以 i i i结尾, B B B集合以 j j j结尾, i &gt; j i&gt;j i>j。我们画个图就可以发现 当 i , j 之 间 有 空 格 的 时 候 , f [ i ] [ j ] = f [ i − 1 ] + a b s ( h [ i ] − h [ i − 1 ] ) , i &gt; j + 1 当i,j之间有空格的时候,f[i][j]=f[i-1]+abs(h[i]-h[i-1]),i&gt;j+1 i,jf[i][j]=f[i1]+abs(h[i]h[i1]),i>j+1没有空格的时候我们要枚举 f [ i ] [ i − 1 ] = m i n ( f [ i − 1 ] [ k ] + a b s ( h [ i ] − h [ k ] ) + a b s ( h [ i ] − h [ k ] ) f[i][i-1]=min(f[i-1][k]+abs(h[i]-h[k])+abs(h[i]-h[k]) f[i][i1]=min(f[i1][k]+abs(h[i]h[k])+abs(h[i]h[k])可以理解为枚举B的长度(A,B)并不绝对,只是表示一个互异关系。
那么我们可以发现前者很好转移,后者不好转移,于是我们思考后者,其实我们发现后者的dp可以单独拿出来做记 g [ i ] = f [ i ] [ i − 1 ] g[i]=f[i][i-1] g[i]=f[i][i1],则 g [ i ] = m i n ( g [ j ] + s u m [ i − 1 ] − s u m [ j ] + a b s ( h [ i ] − h [ j − 1 ] ) g[i]=min(g[j]+sum[i-1]-sum[j]+abs(h[i]-h[j-1]) g[i]=min(g[j]+sum[i1]sum[j]+abs(h[i]h[j1])同样可以理解成枚举集合长度。那么我们把绝对值拿掉就好做了,考虑做一个权值树状数组,一切就显然了。

代码

#include<bits/stdc++.h>
#define maxn 500005
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
LL read(){
    LL res,f=1; char c;
    while(!isdigit(c=getchar())) if(c=='-') f=-1; res=(c^48);
    while(isdigit(c=getchar())) res=(res<<3)+(res<<1)+(c^48);
    return res*f;
}
int p=1,n,h[maxn],d[maxn],rk[maxn];
LL sum[maxn],f[maxn],ans=INF;
struct TR{
    LL arr[maxn];
    TR(){
        memset(arr,0x3f,sizeof arr);
    }
    void update(int x,LL w,int f){
        for(;x>0 && x<=p;x+=f*(x&-x)){
            arr[x]=min(arr[x],w);
        }
    }
    LL query(int x,int f){
        LL res=INF;
        for(;x>0 && x<=p;x-=f*(x&-x)){
            res=min(arr[x],res);
        }
        return res;
    }
}a,b; //a:h[j]<h[i]   b:h[j]>=h[i]
bool cmp(int x,int y){return h[x]<h[y];}
int main(){
    n=read();
    for(int i=1;i<=n;i++){
        h[i]=read(); d[i]=i;
        sum[i]=sum[i-1]+abs(h[i]-h[i-1]);
    }
    sort(d+1,d+n+1,cmp);
    for(int i=1;i<=n;i++) rk[d[i]]=h[d[i]]==h[d[i-1]]?p:++p;
    f[1]=h[1]; a.update(1,0,1); b.update(1,0,-1); ans=sum[n];
    for(int i=2;i<=n;i++){
        f[i]=sum[i-1]+min(a.query(rk[i],1)+h[i],b.query(rk[i],-1)-h[i]);
        a.update(rk[i-1],f[i]-sum[i]-h[i-1],1);
        b.update(rk[i-1],f[i]-sum[i]+h[i-1],-1);
        ans=min(ans,f[i]+sum[n]-sum[i]);
    }
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jarden_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值