清理雪道(bzoj 2502)
Description
滑雪场坐落在FJ省西北部的若干座山上。
从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降的方向。
你的团队负责每周定时清理雪道。你们拥有一架直升飞机,每次飞行可以从总部带一个人降落到滑雪场的某个地点,然后再飞回总部。从降落的地点出发,这个人可以顺着斜坡向下滑行,并清理他所经过的雪道。
由于每次飞行的耗费是固定的,为了最小化耗费,你想知道如何用最少的飞行次数才能完成清理雪道的任务。
Input
Output
输出文件的第一行是一个整数k – 直升飞机的最少飞行次数。
Sample Input
8
1 3
1 7
2 4 5
1 8
1 8
0
2 6 5
0
1 3
1 7
2 4 5
1 8
1 8
0
2 6 5
0
Sample Output
4
/* 有源汇上下界最小流。 设原来的源汇点为SS、TT,超级源汇点为S、T。 先按照可行流建立模型,然后由TT向SS连一条inf的边,跑可行流。 那么现在可行流会聚积在TT到SS的边上,我们需要从TT往SS反流一遍最大流,使正的流量尽量小。 */ #include<cstdio> #include<iostream> #include<cstring> #include<queue> #define N 110 #define M 21000 #define inf 1000000000 using namespace std; int head[N],dis[N],s[N],n,cnt=1,SS,TT,S,T; struct node{int v,f,pre;}e[M]; queue<int> q; void add(int u,int v,int f){ e[++cnt].v=v;e[cnt].f=f;e[cnt].pre=head[u];head[u]=cnt; e[++cnt].v=u;e[cnt].f=0;e[cnt].pre=head[v];head[v]=cnt; } bool bfs(){ memset(dis,-1,sizeof(dis)); q.push(S);dis[S]=0; while(!q.empty()){ int u=q.front();q.pop(); for(int i=head[u];i;i=e[i].pre) if(e[i].f&&dis[e[i].v]==-1){ dis[e[i].v]=dis[u]+1; q.push(e[i].v); } } return dis[T]!=-1; } int dinic(int x,int f){ int rest=f; if(x==T) return f; for(int i=head[x];i;i=e[i].pre){ if(!e[i].f||dis[e[i].v]!=dis[x]+1) continue; int t=dinic(e[i].v,min(rest,e[i].f)); e[i].f-=t;e[i^1].f+=t;rest-=t; } if(rest==f) dis[x]=-1; return f-rest; } int main(){ scanf("%d",&n);SS=0;TT=n+1;S=n+2,T=n+3; for(int i=1;i<=n;i++){ int m,x;scanf("%d",&m); for(int j=1;j<=m;j++){ scanf("%d",&x); s[i]++;s[x]--;add(i,x,inf); } } for(int i=1;i<=n;i++){ if(s[i]>0) add(i,T,s[i]); if(s[i]<0) add(S,i,-s[i]); add(SS,i,inf); add(i,TT,inf); } add(TT,SS,inf); while(bfs()) dinic(S,inf); head[SS]=e[head[SS]].pre; head[TT]=e[head[TT]].pre; for(int i=head[S];i;i=e[i].pre) e[i].f=e[i^1].f=0; for(int i=head[T];i;i=e[i].pre) e[i].f=e[i^1].f=0; int sum=e[cnt].f; add(S,TT,inf);add(SS,T,inf); while(bfs()) sum-=dinic(S,inf); printf("%d",sum); return 0; }