孤独一生

孤独一生
题目描述
下课了,Polo来到球场,但他到了之后才发现…..被放了飞机……

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

球场边有N个台阶拍成一行,第i个台阶的高度是Hi(0< mHi<=10^9),第0个台阶,也就是地面的高度为0。Polo打算把这N个台阶分成两个集合Sa,Sb(可以为空),对于一个台阶集合S={P1,P2,…P|S|},其中P1< P2< …< P|S|,他需要花费

sigma{|S| i=1} {Hpi-Hpi-1}

的体力值来完成。

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

输入
第一行一个数N。

第二行N个整数Hi。

输出
一行一个整数,表示最小的总体力值。

样例输入
3
1 3 1
样例输出
4
提示
对于10%的数据N<=20。

对于20%的数据N<=100。

对于50%的数据N<=5000。

对于100%的数据1<=N<=500000。

来源
中山纪念2014八校

DP50

abs线+h[i]h[i]

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
#define ll long long
ll f[600000];
int h[600000];
ll sum[600000];
int main(){
  int n;
  scanf("%d",&n);
  if (n>5000){
    printf("Hello\n");
    return 0;
  }
  for (int i=1;i<=n;++i) scanf("%d",&h[i]);
  h[0]=0;
  sum[0]=0;
  for (int i=1;i<=n;++i)
    sum[i]=sum[i-1]+abs(h[i]-h[i-1]);
  for (int i=1;i<=n;++i) f[i]=1ll<<60;
  f[0]=0;
  for (int i=1;i<=n;++i){
    for (int j=0;j<i;++j)
      f[i]=min(f[i],f[j]+sum[i-1]-sum[j]+abs(h[i]-h[j-1]));
    //cout<<i<<" "<<f[i]<<endl;
  }
  for(int i=1;i<=n;i++)f[n]=min(f[n],f[i]+sum[n]-sum[i]);
  printf("%lld\n",f[n]);
  //while (1);
  return 0;
}
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define ll long long
ll f[600000];
int h[600000];
ll sum[600000];
int dis[600000];
struct diz{
    ll v,p;
}z[500010];
bool cmp(diz a ,diz b){
    return a.v<b.v;
}
struct node{
    ll T[5000010];
    void build(){
      memset(T,120,sizeof(T));
    }
    void change(int p,int l,int r,int x,ll y){
      T[p]=min(T[p],y);
      if (l==r) return;
      int mid=(l+r)>>1;
      if (x<=mid) change(p+p,l,mid,x,y);
      else change(p+p+1,mid+1,r,x,y);
    }
    ll query(int p,int l,int r,int x,int y){
      if (x>r||y<l) return 1ll<<60;
      if (x<=l&&y>=r) return T[p];
      int mid=(l+r)>>1;
      return min(query(p+p,l,mid,x,y),query(p+p+1,mid+1,r,x,y));
    }
}q1,q2;
int main(){
  int n;
  scanf("%d",&n);
  for (int i=1;i<=n;++i){
    scanf("%d",&h[i]);
    z[i].v=h[i];
    z[i].p=i;
  }
  h[0]=0;
  sum[0]=0;
  for (int i=1;i<=n;++i)
    sum[i]=sum[i-1]+abs(h[i]-h[i-1]);

  sort(z+1,z+1+n,cmp);
  for(int i=1;i<=n;i++) dis[z[i].p]=i;
  q1.build();
  q2.build();
  f[0]=0;
  q1.change(1,0,n,0,0);
  q2.change(1,0,n,0,0);
  for (int i=1;i<=n;++i){
    ll x=q1.query(1,0,n,0,dis[i]);
    ll y=q2.query(1,0,n,dis[i],n);
    //f[i]=min(f[i],f[j]+sum[i-1]-sum[j]+abs(h[i]-h[j-1]));
    f[i]=min(x+sum[i-1]+h[i],y+sum[i-1]-h[i]);
    q1.change(1,0,n,dis[i-1],f[i]-sum[i]-h[i-1]);
    q2.change(1,0,n,dis[i-1],f[i]-sum[i]+h[i-1]);
   // cout<<x+sum[i-1]+h[i]<<" "<<y+sum[i-1]-h[i]<<endl;
  }
  for(int i=1;i<=n;i++)f[n]=min(f[n],f[i]+sum[n]-sum[i]);
  printf("%lld\n",f[n]);
  //while (1);
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值