1001: [BeiJing2006]狼抓兔子
Time Limit: 15 Sec Memory Limit: 162 MB
Submit: 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
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 }
View Code
查了题解说因为图是平面图,所以可以把平面图转对偶图,然后对偶图的最短路就是平面图的最小割,惊了。 上学期离散课上学过平面图转对偶图,但是课时比较赶老师就讲的很水,当时没觉得这东西有什么用然后就没深入研究 想不到转对偶图居然还有这种妙用,不过想一下也很容易理解。
对偶图建图的时候需要小心一点,写的时候调了半天..
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 }
BZOJ1001 [BeiJing2006]狼抓兔子 平面图转对偶图,最小割转最短路
原文:https://www.cnblogs.com/mizersy/p/9564647.html