本文在接下来的一段时间内可能会经常扩充或修改,如有谬误敬请谅解。[2017.11.24]
“拆点”
拆点,顾名思义,就是把一个点拆成两个点。因为在网络流的模型中,割特指边集而非点集,所以想要实现“可以被割掉的点”,就用用到拆点思想。另外,这种方法也可以被理解为:使点同边一样有一个容量上限。
把点A分成两个,一个叫A1,表示A的“入点”;另一个叫A2,表示A的“出点”。A的所有出边都从A2连出,所有入边都连向A1。再连一条边从A1指向A2,其边权为点A的“点容量”。
例题:[USACO5.4]奶牛的电信(copy from 洛谷,特此表示感谢)
P1345 [USACO5.4]奶牛的电信Telecowmunication
题目描述
农夫约翰的奶牛们喜欢通过电邮保持联系,于是她们建立了一个奶牛电脑网络,以便互相交流。这些机器用如下的方式发送电邮:如果存在一个由c台电脑组成的序列a1,a2,…,a(c),且a1与a2相连,a2与a3相连,等等,那么电脑a1和a(c)就可以互发电邮。
很不幸,有时候奶牛会不小心踩到电脑上,农夫约翰的车也可能碾过电脑,这台倒霉的电脑就会坏掉。这意味着这台电脑不能再发送电邮了,于是与这台电脑相关的连接也就不可用了。
有两头奶牛就想:如果我们两个不能互发电邮,至少需要坏掉多少台电脑呢?请编写一个程序为她们计算这个最小值。
以如下网络为例:
1*
/ 3 - 2*
这张图画的是有2条连接的3台电脑。我们想要在电脑1和2之间传送信息。电脑1与3、2与3直接连通。如果电脑3坏了,电脑1与2便不能互发信息了。
输入输出格式
输入格式: 第一行
四个由空格分隔的整数:N,M,c1,c2.N是电脑总数(1<=N<=100),电脑由1到N编号。M是电脑之间连接的总数(1<=M<=600)。最后的两个整数c1和c2是上述两头奶牛使用的电脑编号。连接没有重复且均为双向的(即如果c1与c2相连,那么c2与c1也相连)。两台电脑之间至多有一条连接。电脑c1和c2不会直接相连。第2到M+1行 接下来的M行中,每行包含两台直接相连的电脑的编号。
输出格式: 一个整数表示使电脑c1和c2不能互相通信需要坏掉的电脑数目的最小值。
输入输出样例
样例输入:
3 2 1 2
1 3
2 3
样例输出:
1
附上代码
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
const int maxn=605,INF=0x3f3f3f3f;
struct Dinic{
int n,s,t;
struct Edge{
int from,to,cap,flow;
};
vector<int>G[maxn];
vector<Edge>edges;
int cur[maxn],d[maxn];
bool vis[maxn];
void addedge(int f,int t,int c){
edges.push_back((Edge){f,t,c,0});
edges.push_back((Edge){t,f,0,0});
int m=edges.size();
G[f].push_back(m-2);
G[t].push_back(m-1);
}
bool BFS(){
//memset(d,0,sizeof(d));
memset(vis,0,sizeof(vis));
queue<int>Q;
Q.push(s);d[s]=0;vis[s]=1;
while(!Q.empty()){
int x=Q.front();Q.pop();
for(int i=0;i<G[x].size();i++){
Edge& e=edges[G[x][i]];
if(!vis[e.to] && e.cap>e.flow){
vis[e.to]=1;
d[e.to]=d[x]+1;
Q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int x,int a){
if(x==t || a==0)return a;
int flow=0,f;
for(int& i=cur[x];i<G[x].size();i++){
Edge& e=edges[G[x][i]];
if(e.cap>e.flow && d[e.to]==d[x]+1 && (f=DFS(e.to,min(a,e.cap-e.flow)))>0){
e.flow+=f;
edges[G[x][i]^1].flow-=f;
flow+=f;
a-=f;
if(a==0)break;
}
}
return flow;
}
int MaxFlow(int S,int T){
s=S;t=T;int flow=0;
while(BFS()){
memset(cur,0,sizeof(cur));
flow+=DFS(S,INF);
}
return flow;
}
}dinic;
int main(){
int N,M,c1,c2;scanf("%d%d%d%d",&N,&M,&c1,&c2);
for(int i=1;i<=N;i++){
dinic.addedge(i,i+N,1);
}
for(int i=1;i<=M;i++){
int x,y;scanf("%d%d",&x,&y);
dinic.addedge(x+N,y,INF);
dinic.addedge(y+N,x,INF);
}
int mf=dinic.MaxFlow(c1+N,c2);
printf("%d\n",mf);
return 0;
}
本题求至少要删除多少个点使C1、C2不连通,问题的实质是一个最小割,跑一个最大流即可,因为只允许割点,不允许割边,所以原有的边边权都为INF。点权为1,表示每个点最多只能被删除一次。