BZOJ 1001 [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只狼,才能完全封锁这条道路,你需要帮助狼王安排一个伏击方案,使得在将兔子一网打尽的前提下,参与的狼的数量要最小。因为狼还要去找喜羊羊麻烦.
Solutions
一眼最小割,写个好一点的网络流随意暴力跑一下可以切,然而还有更好的做法
1 #include<map> 2 #include<cmath> 3 #include<ctime> 4 #include<queue> 5 #include<stack> 6 #include<cstdio> 7 #include<climits> 8 #include<iomanip> 9 #include<cstring> 10 #include<cstdlib> 11 #include<iostream> 12 #include<algorithm> 13 14 #define maxn 1000000+5 15 #define maxs 6000000+5 16 #define set(a,b) memset(a,(b),sizeof(a)) 17 #define fr(i,a,b) for(ll i=(a),_end_=(b);i<=_end_;i++) 18 #define rf(i,b,a) for(ll i=(a),_end_=(b);i>=_end_;i--) 19 #define fe(i,a,b) for(int i=first[(b)],_end_=(a);i!=_end_;i=s[i].next) 20 #define fec(i,a,b) for(int &i=cur[(b)],_end_=(a);i!=_end_;i=s[i].next) 21 22 using namespace std; 23 24 typedef long long ll; 25 26 struct sides{ 27 int u,v,c; 28 int next; 29 }s[maxs]; 30 31 32 queue<int> q; 33 34 int h[maxn]; 35 int first[maxn],cur[maxn]; 36 int ind=0; 37 int ans=0; 38 int n,m; 39 40 void add_f(int u,int v,int c){ 41 s[ind].u=u,s[ind].v=v,s[ind].c=c; 42 s[ind].next=first[u],first[u]=ind; 43 ind++; 44 s[ind].u=v,s[ind].v=u,s[ind].c=c; 45 s[ind].next=first[v],first[v]=ind; 46 ind++; 47 } 48 49 void read() 50 { 51 #ifndef ONLINE_JUDGE 52 freopen("1001.in","r",stdin); 53 freopen("1001.out","w",stdout); 54 #endif 55 56 int c; 57 cin >> n >> m ; 58 set(first,-1); 59 fr(i,1,n) 60 fr(j,1,m-1){ 61 cin >> c ; 62 add_f(m*(i-1)+j,m*(i-1)+j+1,c); 63 } 64 fr(i,1,n-1) 65 fr(j,1,m){ 66 cin >> c ; 67 add_f(m*(i-1)+j,m*(i)+j,c); 68 } 69 fr(i,1,n-1) 70 fr(j,1,m-1){ 71 cin >> c ; 72 add_f(m*(i-1)+j,m*(i)+j+1,c); 73 } 74 } 75 76 void write() 77 { 78 cout << ans ; 79 } 80 81 bool bfs(int u) 82 { 83 set(h,-1); 84 h[u]=1; 85 q.push(u); 86 while( !q.empty() ){ 87 int u=q.front();q.pop(); 88 fe(i,-1,u) 89 if( s[i].c && h[s[i].v]==-1 ){ 90 h[s[i].v]=h[u]+1; 91 q.push(s[i].v); 92 } 93 } 94 return h[n*m]!=-1; 95 } 96 97 int dfs(int u,int flow) 98 { 99 if( u==n*m ) return flow; 100 int w,used=0; 101 fec(i,-1,u){ 102 if( h[s[i].v]==h[u]+1 && s[i].c ){ 103 w=dfs(s[i].v,min(flow-used,s[i].c)); 104 s[i].c-=w,s[i^1].c+=w; 105 used+=w; 106 if( used==flow ) return flow; 107 } 108 } 109 if( !used ) h[u]=-1; 110 return used; 111 } 112 113 void dinic() 114 { 115 while( bfs(1) ){ 116 fr(i,1,n*m) 117 cur[i]=first[i]; 118 ans+=dfs(1,INT_MAX); 119 } 120 } 121 122 void work() 123 { 124 dinic(); 125 } 126 127 int main() 128 { 129 read(); 130 work(); 131 write(); 132 return 0; 133 }
然后去学习了以下平面图
简单地,对于任意一个图 G,我们有它的对偶图 G’,即将原图的点与边划分成的平面视作新图 G’ 中的点,反之亦然
新图中的边就是原图相邻两平面,边权是连接这两个平面所割的边
于是 G’ 中的环对应 G 中的割一一对应
然后我们对于新图再连接一下原源点和原汇点,形成一个新的平面,我们将这个平面视作新源点S,无穷大的平面视作新汇点T,然后再把对偶图中S到T直接相连的边删掉
可以知道求原图的最大流就相当于求新图的最短路
详见周冬的论文 《两极相通——浅析最大—最小定理在信息学竞赛中的应用》
然后就划分平面= =,事实上根本分不清……最后膜了黄学长代码才过的= =
1 #include<map> 2 #include<cmath> 3 #include<ctime> 4 #include<queue> 5 #include<stack> 6 #include<cstdio> 7 #include<climits> 8 #include<iomanip> 9 #include<cstring> 10 #include<cstdlib> 11 #include<iostream> 12 #include<algorithm> 13 14 #define maxn 2000000+5 15 #define maxs 8000000+5 16 #define set(a,b) memset(a,(b),sizeof(a)) 17 #define fr(i,a,b) for(ll i=(a),_end_=(b);i<=_end_;i++) 18 #define rf(i,b,a) for(ll i=(a),_end_=(b);i>=_end_;i--) 19 #define fe(i,a,b) for(int i=first[(b)],_end_=(a);i!=_end_;i=s[i].next) 20 #define fec(i,a,b) for(int &i=cur[(b)],_end_=(a);i!=_end_;i=s[i].next) 21 22 using namespace std; 23 24 typedef long long ll; 25 typedef pair<int,int> pii; 26 27 struct sides{ 28 int u,v,w; 29 int next; 30 }s[maxs]; 31 32 queue<int> q; 33 priority_queue<pii,vector<pii>,greater<pii> > q; 34 35 int dis[maxn],inq[maxn]; 36 int first[maxn],ind=0; 37 int n,m,num; 38 39 void add_s(int u,int v,int w){ 40 s[ind].u=u,s[ind].v=v,s[ind].w=w; 41 s[ind].next=first[u],first[u]=ind; 42 ind++; 43 s[ind].u=v,s[ind].v=u,s[ind].w=w; 44 s[ind].next=first[v],first[v]=ind; 45 ind++; 46 } 47 48 void read() 49 { 50 #ifndef ONLINE_JUDGE 51 freopen("1001.in","r",stdin); 52 freopen("1001.out","w",stdout); 53 #endif 54 55 int c; 56 set(first,-1); 57 cin >> n >> m ; 58 num=(n-1)*(m-1)<<1; 59 fr(i,1,m-1){ 60 cin >> c ; 61 add_s(i,num+1,c); 62 } 63 fr(i,1,n-2) 64 fr(j,1,m-1){ 65 cin >> c ; 66 add_s((i<<1)*(m-1)+j,((i<<1)-1)*(m-1)+j,c); 67 } 68 fr(i,1,m-1){ 69 cin >> c ; 70 add_s(0,((n<<1)-3)*(m-1)+i,c); 71 } 72 fr(i,0,n-2) 73 fr(j,1,m){ 74 cin >> c ; 75 if( j==1 ) 76 add_s(0,(i<<1)*(m-1)+m,c); 77 else if( j==m ) 78 add_s((i<<1|1)*(m-1),num+1,c); 79 else 80 add_s((i<<1)*(m-1)+j-1,(i<<1)*(m-1)+j+m-1,c); 81 } 82 fr(i,0,n-1) 83 fr(j,1,m-1){ 84 cin >> c ; 85 add_s((i<<1|1)*(m-1)+j,(i<<1)*(m-1)+j,c); 86 } 87 } 88 89 void write() 90 { 91 cout << dis[num+1] ; 92 } 93 94 void SPFA() 95 { 96 set(dis,127); 97 q.push(0); 98 dis[0]=0; 99 while( !q.empty() ){ 100 int u=q.front();q.pop(); 101 inq[u]=0; 102 fe(i,-1,u) 103 if( dis[s[i].v]>dis[u]+s[i].w ){ 104 dis[s[i].v]=dis[u]+s[i].w; 105 if( !inq[s[i].v] ) 106 q.push(s[i].v); 107 } 108 } 109 } 110 111 void dijkstra() 112 { 113 set(dis,127); 114 q.push(make_pair(0,0)); 115 dis[0]=0; 116 while( !q.empty() ){ 117 pii st=q.top();q.pop(); 118 int sd=st.second; 119 if( inq[sd] ) continue; 120 inq[sd]=1; 121 fe(i,-1,sd) 122 if( dis[s[i].v]>dis[sd]+s[i].w ){ 123 dis[s[i].v]=dis[sd]+s[i].w; 124 q.push(make_pair(dis[s[i].v],s[i].v)); 125 } 126 } 127 } 128 129 void work() 130 { 131 SPFA(); 132 dijkstra(); 133 } 134 135 int main() 136 { 137 read(); 138 work(); 139 write(); 140 return 0; 141 }
//Dijkstra好像比SPFA快一点
然后= =
我就
淡疼地发现
平面图比我的暴力慢 Orz = =