[USACO5.4]Telecowmunication
OJ题号:
洛谷1345
思路:
求无向图最大流最小点割集。
首先将每个点拆成两个,对于自己,连一条容量为$1$的边,对于原来的边,对$(x\prime,y)$$(y\prime,x)$分别连一条容量为$\infty$的边,这样我们就将最小点割转化成了最小边割,根据最大流最小割定理,直接跑最大流即可。
如何求出字典序最小的集?
一个点在最小点割集中当且仅当删去这个点时,最大流变小。因此我们可以从小到大枚举删除每个点,求得删除该点之后的最大流,若最大流变小则将其加入答案集合,恢复残量网络并删除这个点,直到最大流变为$0$。
注:USACO Training原题要求按字典序输出点集,而这题在洛谷只要求输出集合大小,校内OJ又卡评测,故在程序代码中没有体现。
1 #include<queue> 2 #include<cstdio> 3 #include<cctype> 4 #include<vector> 5 #include<cstring> 6 inline int getint() { 7 char ch; 8 while(!isdigit(ch=getchar())); 9 int x=ch^'0'; 10 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 11 return x; 12 } 13 const int inf=0x7fffffff; 14 int s,t; 15 const int E=2600,V=201; 16 struct Edge { 17 int from,to,remain; 18 }; 19 Edge e[E]; 20 std::vector<int> g[V]; 21 int sz=0; 22 inline void add_edge(const int u,const int v,const int w) { 23 e[sz]=(Edge){u,v,w}; 24 g[u].push_back(sz); 25 sz++; 26 } 27 int a[V],p[V]; 28 inline int Augment() { 29 memset(a,0,sizeof a); 30 a[s]=inf; 31 std::queue<int> q; 32 q.push(s); 33 while(!q.empty()) { 34 int x=q.front(); 35 q.pop(); 36 for(unsigned i=0;i<g[x].size();i++) { 37 Edge &y=e[g[x][i]]; 38 if(!a[y.to]&&y.remain) { 39 p[y.to]=g[x][i]; 40 a[y.to]=std::min(y.remain,a[x]); 41 q.push(y.to); 42 } 43 } 44 if(a[t]) break; 45 } 46 return a[t]; 47 } 48 inline int EdmondsKarp() { 49 int maxflow=0; 50 while(int flow=Augment()) { 51 for(int i=t;i!=s;i=e[p[i]].from) { 52 e[p[i]].remain-=flow; 53 e[p[i]^1].remain+=flow; 54 } 55 maxflow+=flow; 56 } 57 return maxflow; 58 } 59 int main() { 60 int n=getint(),m=getint(); 61 s=getint()+n,t=getint(); 62 for(int i=0;i<m;i++) { 63 int u=getint(),v=getint(); 64 add_edge(u+n,v,inf); 65 add_edge(v,u+n,0); 66 add_edge(v+n,u,inf); 67 add_edge(u,v+n,0); 68 } 69 for(int i=1;i<=n;i++) { 70 add_edge(i,i+n,1); 71 add_edge(i+n,i,0); 72 } 73 printf("%d\n",EdmondsKarp()); 74 return 0; 75 }