BZOJ1001 [BeiJing2006]狼抓兔子 平面图转对偶图,最小割转最短路
1001: [BeiJing2006]狼抓兔子
Time Limit: 15 Sec Memory Limit: 162 MBSubmit: 28885 Solved: 7540
[Submit][Status][Discuss]
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
很容易可以看出这是一道求最小割的题,但图太大了网络流没法跑。网上有人网络流水过的,但我写的网络流莫名奇妙一直WA...
1 /************************************************************** 2 Problem: 1001 3 User: mizersy 4 Language: C++ 5 Result: Wrong_Answer 6 ****************************************************************/ 7 8 #include <bits/stdc++.h> 9 using namespace std; 10 const int MAXN = 2000010; 11 const int MAXM = 6200010; 12 const int INF = 0x3f3f3f3f; 13 struct Edge{ 14 int to,next,cap,flow; 15 } edge[MAXM]; 16 int tol; 17 int head[MAXN]; 18 void init(){ 19 tol = 2; 20 memset(head,-1,sizeof(head)); 21 } 22 void addedge(int u,int v,int w,int rw = 0){ 23 edge[tol].to = v; 24 edge[tol].cap = w; 25 edge[tol].flow = 0; 26 edge[tol].next = head[u]; 27 head[u] = tol++; 28 edge[tol].to = u; 29 edge[tol].cap = rw; 30 edge[tol].flow = 0; 31 edge[tol].next = head[v]; 32 head[v] = tol++; 33 } 34 int Q[MAXN]; 35 int dep[MAXN],cur[MAXN],sta[MAXN]; 36 bool bfs(int s,int t,int n){ 37 int front = 0,tail = 0; 38 memset(dep,-1,sizeof(dep[0])*(n+1)); 39 dep[s] = 0; 40 Q[tail++] = s; 41 while(front < tail){ 42 int u = Q[front++]; 43 for(int i = head[u]; i !=-1; i = edge[i].next){ 44 int v = edge[i].to; 45 if(edge[i].cap > edge[i].flow && dep[v] ==-1){ 46 dep[v] = dep[u] + 1; 47 if(v == t) 48 return true; 49 Q[tail++] = v; 50 } 51 } 52 } 53 return false; 54 } 55 int dinic(int s,int t,int n){ 56 int maxflow = 0; 57 while(bfs(s,t,n)){ 58 for(int i = 0; i <= n; i++) cur[i] = head[i]; 59 int u = s, tail = 0; 60 while(cur[s] !=-1){ 61 if(u == t){ 62 int tp = INF; 63 for(int i = tail-1; i >= 0; i--) 64 tp = min(tp,edge[sta[i]].cap-edge[sta[i]].flow); 65 maxflow += tp; 66 for(int i = tail-1; i >= 0; i--){ 67 edge[sta[i]].flow += tp; 68 edge[sta[i]^1].flow-= tp; 69 if(edge[sta[i]].cap-edge[sta[i]].flow == 0) 70 tail = i; 71 } 72 u = edge[sta[tail]^1].to; 73 }else if(cur[u] !=-1 && edge[cur[u]].cap > edge[cur[u]].flow && dep[u] + 1 == dep[edge[cur[u]].to]) 74 { 75 sta[tail++] = cur[u]; 76 u = edge[cur[u]].to; 77 }else{ 78 while(u != s && cur[u] ==-1) 79 u = edge[sta[--tail]^1].to; 80 cur[u] = edge[cur[u]].next; 81 } 82 } 83 } 84 return maxflow; 85 } 86 int n,m,cnt; 87 int a[1005][1005]; 88 89 int point(int i,int j){ 90 return (i-1)*m+j; 91 } 92 93 int main(){ 94 scanf("%d%d",&n,&m); 95 cnt = 0; 96 init(); 97 memset(a,0,sizeof(a)); 98 for (int i = 1;i <= n;++i){ 99 for (int j = 1;j < m;++j){ 100 int w; scanf("%d",&w); 101 addedge(point(i,j),point(i,j+1),w); 102 } 103 } 104 for (int i = 1;i < n;++i){ 105 for (int j = 1;j <= m;++j){ 106 int w; scanf("%d",&w); 107 addedge(point(i,j),point(i+1,j),w); 108 } 109 } 110 for (int i = 1;i < n;++i){ 111 for (int j = 1;j < m;++j){ 112 int w; scanf("%d",&w); 113 addedge(point(i,j),point(i+1,j+1),w); 114 } 115 } 116 printf("%d\n",dinic(1,n*m,n*m)); 117 }
查了题解说因为图是平面图,所以可以把平面图转对偶图,然后对偶图的最短路就是平面图的最小割,惊了。 上学期离散课上学过平面图转对偶图,但是课时比较赶老师就讲的很水,当时没觉得这东西有什么用然后就没深入研究 想不到转对偶图居然还有这种妙用,不过想一下也很容易理解。
对偶图建图的时候需要小心一点,写的时候调了半天..
1 /************************************************************** 2 Problem: 1001 3 User: mizersy 4 Language: C++ 5 Result: Accepted 6 Time:3108 ms 7 Memory:118480 kb 8 ****************************************************************/ 9 10 #include <bits/stdc++.h> 11 using namespace std; 12 const int MAXN = 2000005; 13 struct Edge{int to,next,w;}edge[MAXN*4]; 14 int tol,n,m,w; 15 int head[MAXN],vis[MAXN],dis[MAXN]; 16 void init(){ 17 tol = 0; 18 memset(vis,0,sizeof(vis)); 19 memset(dis,0x3f3f3f3f,sizeof(dis)); 20 memset(head,-1,sizeof(head)); 21 } 22 23 void addedge(int u,int v,int w){ 24 edge[tol] = Edge{v,head[u],w}; 25 head[u] = tol++; 26 edge[tol] = Edge{u,head[v],w}; 27 head[v] = tol++; 28 } 29 30 void getmap(){ 31 for (int j = 1;j < m;++j){ 32 scanf("%d",&w); 33 addedge(0,j*2,w); 34 } 35 for (int i = 2;i < n;++i){ 36 for (int j = 1;j < m;++j){ 37 scanf("%d",&w); 38 addedge(2*(i-2)*(m-1)+j*2-1,2*(i-1)*(m-1)+j*2,w); 39 } 40 } 41 if (n >= 2) 42 for (int j = 1;j < m;++j){ 43 scanf("%d",&w); 44 addedge(2*(n-2)*(m-1)+j*2-1,2*(n-1)*(m-1)+1,w); 45 } 46 for (int i = 1;i < n;++i){ 47 for (int j = 1;j <= m;++j){ 48 scanf("%d",&w); 49 if (j == 1) addedge(2*(n-1)*(m-1)+1,2*(i-1)*(m-1)+1,w); 50 else if (j == m) addedge(2*i*(m-1),0,w); 51 else addedge(2*(i-1)*(m-1)+(j-1)*2,2*(i-1)*(m-1)+(j-1)*2+1,w); 52 } 53 } 54 55 for (int i = 1;i < n;++i){ 56 for (int j = 1;j < m;++j){ 57 scanf("%d",&w); 58 addedge(2*(i-1)*(m-1)+(j-1)*2+1,2*(i-1)*(m-1)+j*2,w); 59 } 60 } 61 } 62 63 int spfa(int s,int t){ 64 queue <int> q; 65 dis[s] = 0; vis[s] = 1; 66 q.push(s); 67 while(!q.empty()){ 68 int u = q.front(); q.pop(); 69 vis[u] = 0; 70 for (int i = head[u];i != -1;i = edge[i].next){ 71 int v = edge[i].to,w = edge[i].w; 72 if (dis[v] > dis[u] + w){ 73 dis[v] = dis[u] + w; 74 if (!vis[v]){ 75 vis[v] = 1; 76 q.push(v); 77 } 78 } 79 } 80 } 81 return dis[t]; 82 } 83 84 int main(){ 85 init(); 86 scanf("%d%d",&n,&m); 87 if (n == 1 || m == 1){ 88 if (n > m) swap(n,m); 89 int ans = 0x3f3f3f3f; 90 for (int i = 1;i < m;++i){ 91 scanf("%d",&w); 92 ans = min(ans,w); 93 } 94 if (ans == 0x3f3f3f3f) ans = 0; 95 printf("%d\n",ans); 96 return 0; 97 } 98 getmap(); 99 printf("%d\n",spfa(0,2*(m-1)*(n-1)+1)); 100 return 0; 101 }