【bzoj4200】【NOI2015】【小园丁与老司机】【dp+最小流】

35 篇文章 0 订阅

Description

小园丁 Mr. S 负责看管一片田野,田野可以看作一个二维平面。田野上有 nn 棵许愿树,编号 1,2,3,…,n1,2,3,…,n,每棵树可以看作平面上的一个点,其中第 ii 棵树 (1≤i≤n1≤i≤n) 位于坐标 (xi,yi)(xi,yi)。任意两棵树的坐标均不相同。
老司机 Mr. P 从原点 (0,0)(0,0) 驾车出发,进行若干轮行动。每一轮,Mr. P 首先选择任意一个满足以下条件的方向:
为左、右、上、左上 45∘45∘ 、右上 45∘45∘ 五个方向之一。
沿此方向前进可以到达一棵他尚未许愿过的树。
完成选择后,Mr. P 沿该方向直线前进,必须到达该方向上距离最近的尚未许愿的树,在树下许愿并继续下一轮行动。如果没有满足条件的方向可供选择,则停止行动。他会采取最优策略,在尽可能多的树下许愿。若最优策略不唯一,可以选择任意一种。
不幸的是,小园丁 Mr. S 发现由于田野土质松软,老司机 Mr. P 的小汽车在每轮行进过程中,都会在田野上留下一条车辙印,一条车辙印可看作以两棵树(或原点和一棵树)为端点的一条线段。
在 Mr. P 之后,还有很多许愿者计划驾车来田野许愿,这些许愿者都会像 Mr. P 一样任选一种最优策略行动。Mr. S 认为非左右方向(即上、左上 45∘45∘ 、右上 45∘45∘ 三个方向)的车辙印很不美观,为了维护田野的形象,他打算租用一些轧路机,在这群许愿者到来之前夯实所有“可能留下非左右方向车辙印”的地面。
“可能留下非左右方向车辙印”的地面应当是田野上的若干条线段,其中每条线段都包含在某一种最优策略的行进路线中。每台轧路机都采取满足以下三个条件的工作模式:
从原点或任意一棵树出发。
只能向上、左上 45∘45∘ 、右上 45∘45∘ 三个方向之一移动,并且只能在树下改变方向或停止。
只能经过“可能留下非左右方向车辙印”的地面,但是同一块地面可以被多台轧路机经过。
现在 Mr. P 和 Mr. S 分别向你提出了一个问题:
请给 Mr .P 指出任意一条最优路线。
请告诉 Mr. S 最少需要租用多少台轧路机。

Input

 输入文件的第 1 行包含 1 个正整数 n,表示许愿树的数量。

接下来 n 行,第 i+1 行包含 2个整数 xi,yi,中间用单个空格隔开,表示第 i 棵许愿树的坐标。

Output

输出文件包括 3 行。
输出文件的第 1 行输出 1 个整数 m,表示 Mr. P 最多能在多少棵树下许愿。
输出文件的第 2 行输出 m 个整数,相邻整数之间用单个空格隔开,表示 Mr. P 应该依次在哪些树下许愿。
输出文件的第 3 行输出 1 个整数,表示 Mr. S 最少需要租用多少台轧路机。

Sample Input

6
-1 1
1 1
-2 2
0 8
0 9
0 10

Sample Output

3
2 1 3
3

explanation

最优路线 2 条可许愿 3 次:(0,0)→(1,1)→(−1,1)→(−2,2)(0,0)→(1,1)→(−1,1)→(−2,2) 或 (0,0)→(0,8)→(0,9)→(0,10)(0,0)→(0,8)→(0,9)→(0,10)。 至少 3 台轧路机,路线是 (0,0)→(1,1)(0,0)→(1,1),(−1,1)→(−2,2)(−1,1)→(−2,2) 和 (0,0)→(0,8)→(0,9)→(0,10)(0,0)→(0,8)→(0,9)→(0,10)。

题解:

首先正着dp出答案.然后再反着dp出所有路径.

dp的时候需要注意:

1.为了保证复杂度可以用一个map记录一下一个点三个方向上的最近点.

2.同行dp的时候从i到j肯定是从i先走到边界再去j.

3.正反dp的时候同行转移是不一样的.

4.dp数组一定要赋成-inf.

dp完之后就是用最少次数覆盖所有路径.这个直接最小流即可.

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#define N 50010
#define M 3000010
#define inf 210000000
using namespace std;
int n,x,y,num,S,T,SS,TT,f[N],g[N],q[N],dp[N],ans,fg[N],h[N],s[N],tr[N],d[N];
int point[N],next[M<<1],cur[N],gap[N],pre[N],dis[N],cnt=1;
struct use{
  int st,en,v;
}e[M<<1];
map<int,int>a,b,c;
struct pt{
  int x,y,id;
}p[N];
int read(){
  int x(0),f(1);char ch=getchar();
  while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
  while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
  return x*f;
}
bool cmp(pt a,pt b){
  if (a.y==b.y) return a.x<b.x;
  else return a.y<b.y;
}
void dp1(){
  int t=0,j,pos;
  for (int i=1;i<=n;i++) f[i]=q[i]=-inf;
  while (t<=n){
    for (j=t;p[j].y==p[t].y;j++);j--;
    for (int i=t;i<=j;i++){
      if (a.find(p[i].x+p[i].y)!=a.end()){
        int u=a[p[i].x+p[i].y];
        if (f[i]<f[u]+1){f[i]=f[u]+1;g[i]=u;}
      }
      a[p[i].x+p[i].y]=i;
      if (b.find(p[i].x-p[i].y)!=b.end()){
        int u=b[p[i].x-p[i].y];
        if (f[i]<f[u]+1){f[i]=f[u]+1;g[i]=u;}
      } 
      b[p[i].x-p[i].y]=i;
      if (c.find(p[i].x)!=c.end()){
        int u=c[p[i].x];
        if (f[i]<f[u]+1){f[i]=f[u]+1;g[i]=u;}
      }
      c[p[i].x]=i;
    }
    pos=t;
    for (int i=t+1;i<=j;i++){
      if (f[pos]<f[i-1]) pos=i-1;
      if (f[pos]+i-t>q[i]) q[i]=f[pos]+i-t,h[i]=pos;
    }
    pos=j;
    for (int i=j-1;i>=t;i--){
      if(f[pos]<f[i+1]) pos=i+1;
      if (f[pos]+j-i>q[i]) q[i]=f[pos]+j-i,h[i]=pos; 
    } 
    for (int i=t;i<=j;i++) if (f[i]<q[i]) f[i]=q[i],fg[i]=1;
    t=j+1;
  }
  for (int i=1;i<=n;i++) ans=max(ans,f[i]);
    cout<<ans<<endl;
}
void dp2(){
  int t=n,j,pos;
  a.clear();b.clear();c.clear();
  for (int i=0;i<=n;i++) q[i]=-inf;
  for (int i=0;i<=n;i++) if (f[i]==ans) dp[i]=1;else dp[i]=-inf;
  while (t>=0){
    for (j=t;p[j].y==p[t].y&&j>=0;j--);j++;
    for (int i=j;i<=t;i++){
      if (a.find(p[i].x+p[i].y)!=a.end()){
        int u=a[p[i].x+p[i].y];
        dp[i]=max(dp[i],dp[u]+1);
      } 
      a[p[i].x+p[i].y]=i;
      if (b.find(p[i].x-p[i].y)!=b.end()){
        int u=b[p[i].x-p[i].y];
        dp[i]=max(dp[i],dp[u]+1);
      } 
      b[p[i].x-p[i].y]=i;
      if (c.find(p[i].x)!=c.end()){
        int u=c[p[i].x];
        dp[i]=max(dp[i],dp[u]+1);
      }
      c[p[i].x]=i;
    }
    pos=j;
    for (int i=j+1;i<=t;i++){
      if (dp[pos]+t-pos<dp[i-1]+t-i+1) pos=i-1;
      if (dp[pos]+t-pos>q[i]) q[i]=dp[pos]+t-pos;
    }
    pos=t;
    for (int i=t-1;i>=j;i--){
      if (dp[pos]+pos-j<dp[i+1]+i+1-j) pos=i+1;
      if (dp[pos]+pos-j>q[i]) q[i]=dp[pos]+pos-j; 
    }
    for (int i=j;i<=t;i++) dp[i]=max(dp[i],q[i]);
    t=j-1;
  } 
} 
void add(int x,int y,int v){
  next[++cnt]=point[x];point[x]=cnt;
  e[cnt].st=x;e[cnt].en=y;e[cnt].v=v;
  next[++cnt]=point[y];point[y]=cnt;
  e[cnt].st=y;e[cnt].en=x;e[cnt].v=0;
}
void build(){
  S=n+1;T=S+1;SS=T+1;TT=SS+1;
  a.clear();b.clear();c.clear();
  for (int i=n;i>=0;i--){
    if (a.find(p[i].x+p[i].y)!=a.end()){
      int u=a[p[i].x+p[i].y];
      if (dp[u]+f[i]==ans) add(i+1,u+1,inf),d[i]--,d[u]++;
    }
    a[p[i].x+p[i].y]=i;
    if (b.find(p[i].x-p[i].y)!=b.end()){
      int u=b[p[i].x-p[i].y];
      if (dp[u]+f[i]==ans) add(i+1,u+1,inf),d[i]--,d[u]++; 
    } 
    b[p[i].x-p[i].y]=i;
    if (c.find(p[i].x)!=c.end()){
      int u=c[p[i].x];
      if (dp[u]+f[i]==ans) add(i+1,u+1,inf),d[i]--,d[u]++;
    }
    c[p[i].x]=i;
  }
  for (int i=0;i<=n;i++) add(S+1,i+1,inf),add(i+1,T+1,inf);
  for (int i=0;i<=T;i++){
    if (d[i]>0) add(SS+1,i+1,d[i]);
    else if (d[i]<0) add(i+1,TT+1,-d[i]);
  }
}
int findl(int x){
  int l=1,r=n,ans;
  while (l<=r){
    int mid=(l+r)>>1;
    if (p[mid].y>=x){r=mid-1;ans=mid;}
    else l=mid+1;
  } 
  return ans;
}
int findr(int x){
  int l=1,r=n,ans;
  while (l<=r){
    int mid=(l+r)>>1;
    if (p[mid].y<=x){l=mid+1;ans=mid;}
    else r=mid-1;
  } 
  return ans;
}
void geth(int x,int y){ 
  int l=findl(p[x].y),r=findr(p[x].y);
  if (x<y){
    for (int i=x;i>=l;i--) tr[++num]=i;
    for (int i=x+1;i<=y;i++) tr[++num]=i;
  }
  else{
    for (int i=x;i<=r;i++) tr[++num]=i;
    for (int i=x-1;i>=y;i--) tr[++num]=i;
  }
}
void getroute(){
  int pos,top(0);
  for (int i=1;i<=n;i++) if (f[i]==ans){pos=i;break;}
  while (pos){
    if (fg[pos]){
      s[++top]=pos;s[++top]=h[pos];
      pos=g[h[pos]];
    }
    else{
      s[++top]=pos;pos=g[pos];
    }
  }
  while (top){
    if (top>1&&p[s[top]].y==p[s[top-1]].y){
      geth(s[top],s[top-1]);
      top-=2;   
    }
    else tr[++num]=s[top],top--;
  }
  for (int i=1;i<=num;i++) printf("%d ",p[tr[i]].id);
  puts("");
}
int isap(int ss,int tt){
  int mn,u=ss,i,ans=0;
  gap[0]=tt;
  for (int i=1;i<=tt;i++) gap[i]=0;
  for (int i=0;i<=tt;i++) dis[i]=0;
  for (int i=0;i<=tt;i++) cur[i]=point[i];
  while (dis[ss]<tt){
    bool f=false;
    for (i=cur[u];i;i=next[i])
      if (e[i].v&&dis[e[i].en]+1==dis[u]){f=true;cur[u]=i;break;}
    if (f){
      pre[u=e[i].en]=i;
      if (u==tt){
        mn=inf;
        for (int i=tt;i!=ss;i=e[pre[i]].st) mn=min(mn,e[pre[i]].v);
        ans+=mn;
        for (int i=tt;i!=ss;i=e[pre[i]].st) e[pre[i]].v-=mn,e[pre[i]^1].v+=mn;
        u=ss;
      }
    }  
    else{
      gap[dis[u]]--;if (!gap[dis[u]]) return ans;
      for (mn=tt,i=point[u];i;i=next[i]) if (e[i].v)mn=min(mn,dis[e[i].en]);
      gap[dis[u]=mn+1]++;cur[u]=point[u];if (u!=ss) u=e[pre[u]].st;
    }
  } 
  return ans;
} 
void solve(){
  int ans;
  ans=isap(SS+1,TT+1);
  add(T+1,S+1,inf);
  ans=isap(SS+1,TT+1);
  printf("%d\n",ans);   
}
int main(){
  n=read(); 
  for (int i=1;i<=n;i++){
    p[i].x=read();p[i].y=read();p[i].id=i;
  }
  sort(p+1,p+n+1,cmp);
  dp1();
  if (ans) getroute();
  dp2();
  build();
  solve();
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值