lis+dp--bzoj1049

传送门

简直神题

直接放别人博客https://www.cnblogs.com/iwtwiioi/p/4160945.html

但我的写法不太一样

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define maxn 35005
#define LL long long
using namespace std;
int n,a[maxn],f[maxn],cnt,head[maxn],mx,mn[maxn];
LL g[maxn],s1[maxn],s2[maxn];

inline int rd(){
  int x=0,f=1;char c=' ';
  while(c<'0' || c>'9') {if(c=='-')f=-1;c=getchar();}
  while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
  return x*f;
}

struct EDGE{
  int to,nxt;
}edge[maxn];

inline void add(int x,int y){
  edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt;
}

inline int find(int x){
  int l=1,r=mx,t=0;
  while(l<=r){
    int mid=(l+r)>>1;
    if(mn[mid]<=x) l=mid+1,t=mid;
    else r=mid-1;
  }
  return t;
}

inline void dp(){//先解决第一问,求出f数组
  memset(mn,0x3f,sizeof mn);
  mn[0]=-1<<30;
  for(int i=1;i<=n;i++){
    int tmp=find(a[i]);
    f[i]=tmp+1;
    mx=max(mx,tmp+1);
    mn[tmp+1]=min(mn[tmp+1],a[i]);
  }
}

inline void solve(){
  for(int i=n;i>=0;i--) add(f[i],i),g[i]=1LL<<60;//向可转移的点连边
  g[0]=0,a[0]=-1<<30;
  for(int x=1;x<=n;x++){
    for(int i=head[f[x]-1];i;i=edge[i].nxt){
      int p=edge[i].to;
      if(p>x) break;
      if(a[p]>a[x]) continue;
      for(int j=p;j<=x;j++)
        s1[j]=abs(a[p]-a[j]),s2[j]=abs(a[x]-a[j]);
      for(int j=p+1;j<=x;j++)//前缀和
        s1[j]+=s1[j-1],s2[j]+=s2[j-1];
      for(int j=p;j<x;j++)
        g[x]=min(g[x],g[p]+s1[j]-s1[p]+s2[x]-s2[j]);//枚举最优的点
    }
  }
}

int main(){
  n=rd(); mx=-1e9;
  for(int i=1;i<=n;i++)
    a[i]=rd()-i;
  a[++n]=1<<30;
  dp();
  solve();
  printf("%d\n%lld\n",n-f[n],g[n]);
  return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值