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  = =

posted @ 2015-06-24 16:09  ST_Saint  阅读(179)  评论(0编辑  收藏  举报