BZOJ1001: [BeiJing2006]狼抓兔子
Description
现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓兔子还是比较在行的,
而且现在的兔子还比较笨,它们只有两个窝,现在你做为狼王,面对下面这样一个网格的地形:
左上角点为(1,1),右下角点为(N,M)(上图中N=4,M=5).有以下三种类型的道路
1:(x,y)<==>(x+1,y)
2:(x,y)<==>(x,y+1)
3:(x,y)<==>(x+1,y+1)
道路上的权值表示这条路上最多能够通过的兔子数,道路是无向的. 左上角和右下角为兔子的两个窝,
开始时所有的兔子都聚集在左上角(1,1)的窝里,现在它们要跑到右下解(N,M)的窝中去,狼王开始伏击
这些兔子.当然为了保险起见,如果一条道路上最多通过的兔子数为K,狼王需要安排同样数量的K只狼,
才能完全封锁这条道路,你需要帮助狼王安排一个伏击方案,使得在将兔子一网打尽的前提下,参与的
狼的数量要最小。因为狼还要去找喜羊羊麻烦.
Input
第一行为N,M.表示网格的大小,N,M均小于等于1000.
接下来分三部分
第一部分共N行,每行M-1个数,表示横向道路的权值.
第二部分共N-1行,每行M个数,表示纵向道路的权值.
第三部分共N-1行,每行M-1个数,表示斜向道路的权值.
输入文件保证不超过10M
Output
输出一个整数,表示参与伏击的狼的最小数量.
Sample Input
3 4
5 6 4
4 3 1
7 5 3
5 6 7 8
8 7 6 5
5 5 5
6 6 6
5 6 4
4 3 1
7 5 3
5 6 7 8
8 7 6 5
5 5 5
6 6 6
Sample Output
14
题解Here!
题目要求最小割,而最小割等于最大流,于是直接 Dinic 即可。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<queue> #define MAXN 1010 #define MAX 999999999 #define point(x,y) ((x-1)*m+y) using namespace std; int n,m,c=2,s,t; int head[MAXN*MAXN],deep[MAXN*MAXN]; struct node{ int next,to,w; }a[MAXN*MAXN*12]; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } inline void add(int u,int v,int w){ a[c].to=v;a[c].w=w;a[c].next=head[u];head[u]=c++; a[c].to=u;a[c].w=0;a[c].next=head[v];head[v]=c++; } bool bfs(){ int u,v; queue<int> q; for(int i=s;i<=t;i++)deep[i]=0; deep[s]=1; q.push(s); while(!q.empty()){ u=q.front(); q.pop(); for(int i=head[u];i;i=a[i].next){ v=a[i].to; if(a[i].w&&!deep[v]){ deep[v]=deep[u]+1; if(v==t)return true; q.push(v); } } } return false; } int dfs(int x,int limit){ if(x==t)return limit; int v,sum,cost=0; for(int i=head[x];i;i=a[i].next){ v=a[i].to; if(a[i].w&&deep[v]==deep[x]+1){ sum=dfs(v,min(a[i].w,limit-cost)); if(sum>0){ a[i].w-=sum; a[i^1].w+=sum; cost+=sum; if(cost==limit)break; } else deep[v]=-1; } } return cost; } int dinic(){ int ans=0; while(bfs())ans+=dfs(s,MAX); return ans; } int main(){ int u,v,w; n=read();m=read(); s=1;t=n*m; for(int i=1;i<=n;i++) for(int j=1;j<m;j++){ w=read(); u=point(i,j);v=point(i,j+1); add(u,v,w);add(v,u,w); } for(int i=1;i<n;i++) for(int j=1;j<=m;j++){ w=read(); u=point(i,j);v=point(i+1,j); add(u,v,w);add(v,u,w); } for(int i=1;i<n;i++) for(int j=1;j<m;j++){ w=read(); u=point(i,j);v=point(i+1,j+1); add(u,v,w);add(v,u,w); } printf("%d\n",dinic()); return 0; }
当然,如果数据再大一点,网络就会TLE,怎么办呢?
这是一张网格图,网格图的最小割可以转化为:对偶图的最短路。
对偶图:将每一块空白区域转为点,点与点之间边的长度就是原图中被其割的边的流量。
这里就不附上代码了,如何转为对偶图是其重点。