BZOJ 1001: [BeiJing2006]狼抓兔子最大流/SPFA+最小割,多解
Posted Angel_Kitty
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 1001: [BeiJing2006]狼抓兔子最大流/SPFA+最小割,多解相关的知识,希望对你有一定的参考价值。
1001: [BeiJing2006]狼抓兔子
Time Limit: 15 Sec Memory Limit: 162 MBSubmit: 23822 Solved: 6012
[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
HINT
2015.4.16新加数据一组,可能会卡掉从前可以过的程序。
Source
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1001
分析:最大流写法如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 int n,m; 5 inline int read() 6 { 7 int x=0,f=1; 8 char ch=getchar(); 9 while(ch<‘0‘||ch>‘9‘) 10 { 11 if(ch==‘-‘) 12 f=-1; 13 ch=getchar(); 14 } 15 while(ch>=‘0‘&&ch<=‘9‘) 16 { 17 x=x*10+ch-‘0‘; 18 ch=getchar(); 19 } 20 return x*f; 21 } 22 inline void write(int x) 23 { 24 if(x<0) 25 { 26 putchar(‘-‘); 27 x=-x; 28 } 29 if(x>9) 30 { 31 write(x/10); 32 } 33 putchar(x%10+‘0‘); 34 } 35 int ne; 36 const int N=1000010; 37 struct data 38 { 39 int to,next,v; 40 }e[N<<3]; 41 int head[N]; 42 int h[N],q[N],ans; 43 inline void update(int u,int v,int w) 44 { 45 ne++; 46 e[ne].to=v; 47 e[ne].v=w; 48 e[ne].next=head[u]; 49 head[u]=ne; 50 } 51 inline bool BFS() 52 { 53 int now,i; 54 memset(h,-1,sizeof(h)); 55 int t=0,w=1; 56 q[t]=1; 57 h[1]=0; 58 while(t<w) 59 { 60 now=q[t]; 61 t++; 62 i=head[now]; 63 while(i) 64 { 65 if(e[i].v&&h[e[i].to]<0) 66 { 67 q[w++]=e[i].to; 68 h[e[i].to]=h[now]+1; 69 } 70 i=e[i].next; 71 } 72 } 73 if(h[n*m]==-1) 74 return false; 75 return true; 76 } 77 inline int DFS(int x,int f) 78 { 79 if(x==n*m) 80 return f; 81 int i=head[x]; 82 int w,used=0; 83 while(i) 84 { 85 if(e[i].v&&h[e[i].to]==h[x]+1) 86 { 87 w=f-used; 88 w=DFS(e[i].to,min(w,e[i].v)); 89 e[i].v-=w; 90 e[i+1].v+=w; 91 used+=w; 92 if(used==f) 93 return f; 94 } 95 i=e[i].next; 96 } 97 if(!used) 98 h[x]=-1; 99 return used; 100 } 101 inline void dinic() 102 { 103 while(BFS()) 104 { 105 ans+=DFS(1,0x7f7f7f7f); 106 } 107 } 108 int main() 109 { 110 n=read(); 111 m=read(); 112 int x; 113 for(int i=1;i<=n;i++) 114 { 115 for(int j=1;j<m;j++) 116 { 117 x=read(); 118 update(m*(i-1)+j,m*(i-1)+j+1,x); 119 update(m*(i-1)+j+1,m*(i-1)+j,x); 120 } 121 } 122 for(int i=1;i<n;i++) 123 { 124 for(int j=1;j<=m;j++) 125 { 126 x=read(); 127 update(m*(i-1)+j,m*(i)+j,x); 128 update(m*(i)+j,m*(i-1)+j,x); 129 } 130 } 131 for(int i=1;i<n;i++) 132 { 133 for(int j=1;j<m;j++) 134 { 135 x=read(); 136 update(m*(i-1)+j,m*(i)+j+1,x); 137 update(m*(i)+j+1,m*(i-1)+j,x); 138 } 139 } 140 dinic(); 141 write(ans); 142 return 0; 143 }
分析:最小割,上网看了别人的博客,学习到了s-t平面图的最小割的解法,把原图中的面看作点,起点和终点都等同于最外面的那一个面,原图中一条边权值为w,新图中就等同于此边在平面图中分割开的两个面(即新图中两个点)连一条边,权值为w。建模完成后,新图中的起点和终点的一条路径就穿插过原图的一些边,即一条路径等于原图中的一个割,所以最小割就等于新图的最短路径长度。确实很厉害。
下面给出SPFA+最小割代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 inline int read() 5 { 6 int x=0,f=1; 7 char ch=getchar(); 8 while(ch<‘0‘||ch>‘9‘) 9 { 10 if(ch==‘-‘) 11 f=-1; 12 ch=getchar(); 13 } 14 while(ch>=‘0‘&&ch<=‘9‘) 15 { 16 x=x*10+ch-‘0‘; 17 ch=getchar(); 18 } 19 return x*f; 20 } 21 inline void write(int x) 22 { 23 if(x<0) 24 { 25 putchar(‘-‘); 26 x=-x; 27 } 28 if(x>9) 29 { 30 write(x/10); 31 } 32 putchar(x%10+‘0‘); 33 } 34 #define M 2000001 35 int n,m,nm; 36 struct data 37 { 38 int to,next,v; 39 }e[4*M]; 40 int dis[M],q[M],head[M]; 41 bool flag[M]; 42 int ne; 43 inline void update(int u,int v,int w) 44 { 45 ne++; 46 e[ne].to=v; 47 e[ne].v=w; 48 e[ne].next=head[u]; 49 head[u]=ne; 50 ne++; 51 e[ne].to=u; 52 e[ne].v=w; 53 e[ne].next=head[v]; 54 head[v]=ne; 55 } 56 inline void spfa() 57 { 58 memset(dis,0x3f,sizeof(dis)); 59 int i,t=0,w=1; 60 dis[0]=q[w]=0;flag[0]=1; 61 while(t!=w) 62 { 63 int u=q[t++]; 64 flag[u]=0; 65 if(t==M)t=0; 66 for(int i=head[u];i;i=e[i].next) 67 { 68 int v=e[i].to; 69 if(dis[v]>dis[u]+e[i].v) 70 { 71 dis[v]=dis[u]+e[i].v; 72 if(flag[v]==0) 73 { 74 flag[v]=1; 75 q[w++]=v; 76 if(w==M)w=0; 77 } 78 } 79 } 80 } 81 } 82 int main() 83 { 84 n=read(); 85 m=read(); 86 nm=(n*m-m-n+1)<<1; 87 int x; 88 for(int j=1;j<m;j++) 89 { 90 x=read(); 91 update(j,nm+1,x); 92 } 93 for(int i=1;i<n-1;i++) 94 { 95 for(int j=1;j<m;j++) 96 { 97 x=read(); 98 update((i<<1)*(m-1)+j,((i<<1)-1)*(m-1)+j,x); 99 } 100 } 101 for(int j=1;j<m;j++) 102 { 103 x=read(); 104 update(0,((n<<1)-3)*(m-1)+j,x); 105 } 106 for(int i=0;i<n-1;i++) 107 { 108 for(int j=1;j<=m;j++) 109 { 110 x=read(); 111 if(j==1) 112 update(0,(i<<1)*(m-1)+m,x); 113 else if(j==m) 114 update((i<<1|1)*(m-1),nm+1,x); 115 else 116 update((i<<1)*(m-1)+j-1,(i<<1)*(m-1)+j+m-1,x); 117 } 118 } 119 for(int i=0;i<n-1;i++) 120 { 121 for(int j=1;j<m;j++) 122 { 123 x=read(); 124 update((i<<1|1)*(m-1)+j,(i<<1)*(m-1)+j,x); 125 } 126 } 127 spfa(); 128 write(dis[nm+1]); 129 return 0; 130 }
以上是关于BZOJ 1001: [BeiJing2006]狼抓兔子最大流/SPFA+最小割,多解的主要内容,如果未能解决你的问题,请参考以下文章