【bzoj2502】【清理雪道】【最小流】

Description

        滑雪场坐落在 FJ 省西北部的若干座山上。
从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降的方向。
你的团队负责每周定时清理雪道。你们拥有一架直升飞机,每次飞行可以从总部带一个人降落到滑雪场的某个地点,然后再飞回总部。从降落的地点出发,这个人可以顺着斜坡向下滑行,并清理他所经过的雪道。
由于每次飞行的耗费是固定的,为了最小化耗费,你想知道如何用最少的飞行次数才能完成清理雪道的任务。

Input

输入文件的第一行包含一个整数 n  (2 <= n <= 100) –  代表滑雪场的地点的数量。接下来的 n 行,描述 1~n 号地点出发的斜坡,第 i 行的第一个数为 mi  (0 <= mi < n ,后面共有 mi 个整数,由空格隔开,每个整数 aij 互不相同,代表从地点 i 下降到地点 aij 的斜坡。每个地点至少有一个斜坡与之相连。

Output

 
        输出文件的第一行是一个整数 k  –  直升飞机的最少飞行次数。

Sample Input

8
1 3
1 7
2 4 5
1 8
1 8
0
2 6 5
0

Sample Output

4

题解:

裸的最小流

具体做法就是

1.新建源汇S,T,按有上下界的方法建图.

2.做一次S->T的最大流.

3.原图中的汇点t向原图中的源点s连一条inf的边.

4.再做一次S->T的最大流就是答案.

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 200
#define M 20000 
#define inf 210000000
using namespace std;
int x,y,S,T,n,s,t,num,d[N],point[N],next[M<<1],cnt=1;
int cur[N],pre[N],gap[N],dis[N],ans;
struct use{
  int st,en,v;
}e[M<<1];
int read(){
  int x(0);char ch=getchar();
  while (ch<'0'||ch>'9') ch=getchar();
  while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
  return x;
}
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;
}
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=1;i<=tt;i++) dis[i]=0;
  for (int i=1;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;
} 
int main(){
   n=read();s=n+1;t=s+1;S=t+1;T=S+1;
   for (int i=1;i<=n;i++){
      num=read();
      for (int j=1;j<=num;j++){
        x=read();
        add(i,x,inf);
        d[i]--;d[x]++;
      }
   }    
   for (int i=1;i<=n;i++) add(s,i,inf),add(i,t,inf);
   for (int i=1;i<=n;i++){
     if (d[i]>0) add(S,i,d[i]);
     else if (d[i]<0) add(i,T,-d[i]);
   }
   ans=isap(S,T);
   add(t,s,inf);
   ans=isap(S,T);
   cout<<ans<<endl;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值