BZOJ1001 [BeiJing2006]狼抓兔子 平面图转对偶图,最小割转最短路

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 }

 

posted @ 2018-08-31 11:02  mizersy  阅读(174)  评论(0编辑  收藏  举报