POJ 2914 Minimum Cut 全局最小割

题目: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];//v数组是马甲数组,dis数组用来表示该点与A集合中所有点之间的边的长度之和  
bool vis[N];//用来标记是否该点加入了A集合  
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;//pre用来表示之前加入A集合的点,我们每次都以0点为第一个加入A集合的点  
        memset(vis, 0, sizeof vis);  
        memset(dis, 0, sizeof dis);
        vis[v[pre]] = true;  
        for(int i = 1; i < n; i++)  
        {  
            k = -1;  
            for(int j = 1; j < n; j++)//根据之前加入的点,要更新dis数组,并找到最大的dis  
                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;//标记该点已经加入A集合  
            if(i == n - 1)//最后一次加入的点就要更新答案了  
            {  
                res = min(res, dis[v[k]]);  
                for(int j = 0; j < n; j++)//将该点合并到pre上,相应的边权就要合并  
                {  
                    mpa[v[pre]][v[j]] += mpa[v[j]][v[k]];  
                    mpa[v[j]][v[pre]] += mpa[v[j]][v[k]];  
                }  
                v[k] = v[--n];//删除点v[k],把最后一个点扔到v[k]上  
            }  
            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;  
}  




  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值