原来刚学网络流的时候10分钟搞定的…今天突然想了半天没想懂QAQ….果然还是蒟蒻本色…
这道题的做法其实说起来也很简单,就是:将点x+n和y,y+n和x连边权为INF,再将每一个点i和i+n连边权为1的边,以c1+n为源点,c2为汇点跑最大流就可以了。
为什么要拆点?因为我们要保证每个点相当于只能被删除一次,删除这条边就相当于删了这个点。
怎么看待原图中本来就存在的边呢?它们只是有一个联通的作用,对于流量并没有限制,所以明确一点:这些边加入网络中限制应该为无限大。同时,也保证了不会去删这些边。
假设现在要从原图中添加一条从x到y的有向边(这道题是无向边,再依下面的方法添加一个y到x的就行了)到网络中去,对于点y来说,这条边的加入不应该影响通过它的流量限制(就是前面连的那个1)发生变化,所以前面那条y到y+n的边应该接在这条边的后面,所以这条边的终点连向网络中的y,相反的,这条边应该受到x的(前面连的1)限制。因为,假设x已经被删(x到x+n满流),那么这条边再加不加都是没有变化的。所以,这条边在网络中的起点应该是x+n,这样才保证受到限制。
代码:
#include<cstdio>
#include<iostream>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
struct edge{
int to,cap,rev;
};
vector<edge>G[210];
int level[205],iter[205];
int n,m,c1,c2;
void addedge(int from,int to,int cap)
{
G[from].push_back(edge{to,cap,(int)G[to].size()});
G[to].push_back(edge{from,0,(int)G[from].size()-1});//反向容量为0!!
}
void bfs(int s)
{
memset(level,-1,sizeof(level));
queue<int>que;
level[s]=0;que.push(s);
while(!que.empty()){
int t=que.front();que.pop();
for(int i=0;i<G[t].size();i++){
edge e=G[t][i];
if(e.cap>0&&level[e.to]<0){
level[e.to]=level[t]+1;
que.push(e.to);
}
}
}
}
int dfs(int v,int t,int f)
{
if(v==t)return f;
for(int&i=iter[v];i<G[v].size();i++){//注意传引用!
edge&e=G[v][i];
if(e.cap>0&&level[v]<level[e.to]){
int d=dfs(e.to,t,min(f,e.cap));
if(d>0){
e.cap-=d;
G[e.to][e.rev].cap+=d;
return d;
}
}
}
return 0;//不要漏了这个,很多时候可能是无法增广的
}
int maxflow(int s,int t){
int flow=0;
for(;;){
bfs(s);
if(level[t]<0)return flow;
memset(iter,0,sizeof(iter));
int f;
while(f=dfs(s,t,0x7f7f7f7f))
flow+=f;
}
}
int main()
{
int i,j;
cin>>n>>m>>c1>>c2;
for(i=1;i<=n;i++)
addedge(i,i+n,1);
for(i=1;i<=m;i++){
int a,b;cin>>a>>b;
addedge(a+n,b,0x7f7f7f7f);
addedge(b+n,a,0x7f7f7f7f);
}
int ans=maxflow(c1+n,c2);
cout<<ans<<endl;
return 0;
}