POJ 2914 Minimum Cut 全局最小割

时间:2022-05-05 04:28:06

题目:http://poj.org/problem?id=2914

题意:给定一个无向图,两点之间可以有很多边连接,问至少去掉多少条边可以把图分成两个不相连的子图

思路:可以把边数直接看成一条带权边,全局最小割模板题。

转自大牛博客:

算法基于这样一个定理:对于任意s, t   V ∈ ,全局最小割或者等于原图的s-t 最小割,或者等于将原图进行 Contract(s, 
t)操作所得的图的全局最小割。 

算法框架: 
1. 设当前找到的最小割MinCut 为+∞ 
2. 在 G中求出任意 s-t 最小割 c,MinCut = min(MinCut, c)   
3. 对 G作 Contract(s, t)操作,得到 G'=(V', E'),若|V'| > 1,则G=G'并转 2,否则MinCut 为原图的全局最
小割 

Contract 操作定义: 
若不存在边(p, q),则定义边(p, q)权值w(p, q) = 0 
Contract(a, b): 删掉点 a, b 及边(a, b),加入新节点 c,对于任意 v V ∈ ,w(v, c) = w(c, v) = w(a, v) + w(b, 
v) 

求 G=(V, E)中任意 s-t 最小割的算法: 
定义w(A, x) = ∑w(v[i], x),v[i] ∈ A  
定义 Ax 为在x 前加入 A 的所有点的集合(不包括 x) 
1. 令集合 A={a},a为 V中任意点 
2. 选取 V - A中的 w(A, x)最大的点 x加入集合 A 
3. 若|A|=|V|,结束 

令倒数第二个加入 A的点为 s,最后一个加入 A的点为 t,则s-t 最小割为 w(At, t)

证明的话可以看这一篇:http://wenku.baidu.com/link?url=D-qlRFV-ICkB7jEVB3HCYgiheZdBne4SL7LTUTbF9d9xzIVeZLoCD4mYT0ZAr_14masqMJ67HbeEbzEgjpVYRi2oJmT4zY7F2Bhnxitji4i

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define debug() puts("here")
using namespace std;

const int N = 510, INF = 0x3f3f3f3f;
int mpa[N][N], dis[N], v[N];
bool vis[N];
int stoer_wagner(int n)
{
int res = INF;
for(int i = 0; i < n; i++) v[i] = i;
while(n > 1)
{
int k, pre = 0;
memset(vis, 0, sizeof vis);
memset(dis, 0, sizeof dis);
for(int i = 1; i < n; i++)
{
k = -1;
for(int j = 1; j < n; j++)
if(! vis[v[j]])
{
dis[v[j]] += mpa[v[pre]][v[j]];
if(k == -1 || dis[v[k]] < dis[v[j]]) k = j;
}
vis[v[k]] = true;
if(i == n - 1)
{
res = min(res, dis[v[k]]);
for(int j = 0; j < n; j++)
{
mpa[v[pre]][v[j]] += mpa[v[j]][v[k]];
mpa[v[j]][v[pre]] += mpa[v[j]][v[k]];
}
v[k] = v[--n];
}
pre = k;
}
}
return res;
}
int main()
{
int n, m, a, b, c;
while(~ scanf("%d%d", &n, &m))
{
memset(mpa, 0, sizeof mpa);
for(int i = 0; i < m; i++)
{
scanf("%d%d%d", &a, &b, &c);
mpa[a][b] += c, mpa[b][a] += c;
}
printf("%d\n", stoer_wagner(n));
}
return 0;
}