P1345 [USACO5.4]奶牛的电信Telecowmunication
最小割,我们建立一个超级源点和超级汇点,做一下最小割,即可得到通过割边使得整张图变成两个完全不相连的集合的最小花费。
同样的,我们也可以指定两个点为源点和汇点,求得通过割边,使得两个点不再连通的最小花费。
(黑边的边权为INF,黄边的边权为1)
因为求的是割掉点的最小个数,而我们的最小割是割边,所以我们就可以使用点边转化技巧 - 割点,将点割开成两个,中间连一条权值为1的边,意味着我们可以割掉这条边。其余的本来就连好的不能割的边我们就设置权值为INF,这样就不会误割。
最后最小割割掉的边实际上就是割掉那一个点。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const int N = 500007, M = 5000007, INF = 0x3f3f3f3f;
int n, m, S, T;
namespace dinic{
const int N = 500007, M = 5000007, INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f;
int head[N], ver[M], nex[M], tot, cur[N];
ll edge[M];
ll maxflow;
int deep[M];
int n, m, S, T;
void add(int x, int y, int z, bool o = 1)
{
ver[tot] = y;
edge[tot] = z;
nex[tot] = head[x];
head[x] = tot ++ ;
if(o)add(y, x, 0, 0);
}
bool bfs(){
memset(deep, 0x3f, sizeof deep);
deep[S] = 0;
queue<int>q;
q.push(S);
while(q.size()){
int x = q.front();
q.pop();
for(int i = head[x] ; ~i; i = nex[i]){
int y = ver[i], z = edge[i];
if(z > 0 && deep[y] == INF){
q.push(y);
deep[y] = deep[x] + 1;
cur[i] = head[i];
if(y == T)return true;
}
}
}
return false;
}
ll dfs(int x, ll flow)
{
if(x == T)return flow;
ll ans = 0, i, k;
for(i = cur[x]; ~i && flow; i = nex[i]){
//cur[x] = i;//当前弧优化
int y = ver[i];
ll z = edge[i];
if(z > 0 && deep[y] == deep[x] + 1){
k = dfs(y, min(flow, z));
if(k == 0)deep[y] = INF;
edge[i] -= k;
edge[i ^ 1] += k;
ans += k;
flow -= k;
}
}
return ans;
}
void init(int _n, int _S, int _T)
{
n = _n, S = _S, T = _T;
memset(head, -1, sizeof head);
tot = maxflow = 0;
}
void main()
{
while(bfs()){
for(int i = 1; i <= n; ++ i)
cur[i] = head[i];
maxflow += dfs(S, LINF);
}
}
}
int main()
{
scanf("%d%d%d%d", &n, &m, &S, &T);
S += n;//从S的出点到T的入点
dinic::init(2 * n * 10, S, T);
for(int i = 1;i <= n;++ i){
dinic::add(i, i + n, 1);
}
for(int i = 1; i<= m; ++i){
int x, y;
scanf("%d%d", &x, &y);//双向图
dinic::add(x + n, y, INF);
dinic::add(y + n, x, INF);
}
dinic::main();
printf("%lld\n", dinic::maxflow);
return 0;
}