poj 2914 Minimum Cut 【无向图全局最小割 Stoer-wagner算法】

Minimum Cut
Time Limit: 10000MS Memory Limit: 65536K
Total Submissions: 8379 Accepted: 3516
Case Time Limit: 5000MS

Description

Given an undirected graph, in which two vertices can be connected by multiple edges, what is the size of the minimum cut of the graph? i.e. how many edges must be removed at least to disconnect the graph into two subgraphs?

Input

Input contains multiple test cases. Each test case starts with two integers N and M (2 ≤ N ≤ 500, 0 ≤ M ≤ N × (N − 1) ⁄ 2) in one line, where N is the number of vertices. Following are M lines, each line contains M integersAB and C (0 ≤ AB < NA ≠ BC > 0), meaning that there C edges connecting vertices A and B.

Output

There is only one line for each test case, which contains the size of the minimum cut of the graph. If the graph is disconnected, print 0.

Sample Input

3 3
0 1 1
1 2 1
2 0 1
4 3
0 1 1
1 2 1
2 3 1
8 14
0 1 1
0 2 1
0 3 1
1 2 1
1 3 1
2 3 1
4 5 1
4 6 1
4 7 1
5 6 1
5 7 1
6 7 1
4 0 1
7 3 1

Sample Output

2
1

2

题意:给出一个N个点、M对联系关系的无向图。其中每对联系关系a b c代表a和b之间有c条边。问你使图不连通至少去掉多少条边。

时间复杂度分析:最大流增广路算法需要O((N^2)M),当图最复杂时M 近似= N^N,那么复杂度为O(N^5)。加上枚举源、汇,绝对TLE。最高标号预进流算法我不会,再说这个最牛的算法也会TLE o(╯□╰)o

全局最小割Stoer-Wagner算法实现 时间复杂度O(N^3) 优化后可以达到O(N^2log(N))

用wage数组记录点的连通度,vis数组标记点是否在集合里面,In数组表示点被其它点合并。

分两步

一:找到S - T的最小割Mincut,其中S 和 T为最后并入集合的两个点。

1,初始化数组vis 和 wage;

2,遍历所有不在集合且没有被合并的点,找到最大wage值的点Next,并记录Mincut、S和T;

3,Next并入集合,叠加与Next相连的所有点(不在集合 且 没有被合并),更新这些点的wage值;

4,重复操作2和3一共N次 或者 找不到新的Next值时 跳出,返回Mincut;

二、找全局最小割ans,需要重复第一步N-1次,因为每次合并一个点,最多合并N-1个点;

1,每次对返回的Mincut,更新ans = min(ans, Mincut),当然ans为0时,说明图不连通;

2,把点T合并到S点,操作有:对 所有没有被合并的点j,Map[S][j] += Map[T][j]。

证明讲解:

讲解1

讲解2

AC代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 500+10
#define INF 0x3f3f3f3f
using namespace std;
int Map[MAXN][MAXN];
bool vis[MAXN];//是否已并入集合
int wage[MAXN];//记录每个点的连通度
bool In[MAXN];//该点是否已经合并到其它点
int N, M;
void getMap()
{
    memset(Map, 0, sizeof(Map));
    int a, b, c;
    for(int i = 0; i < M; i++)
    {
        scanf("%d%d%d", &a, &b, &c);
        a++, b++;
        Map[a][b] += c;
        Map[b][a] += c;
    }
}
int S, T;//记录每次找s-t割  所遍历的最后两个点
int work()
{
    int Mincut;//每一步找到的s-t割
    memset(wage, 0, sizeof(wage));
    memset(vis, false, sizeof(vis));
    int Next;
    for(int i = 1; i <= N; i++)
    {
        int Max = -INF;
        for(int j = 1; j <= N; j++)
        {
            if(!In[j] && !vis[j] && Max < wage[j])//找最大的wage值
            {
                Next = j;
                Max = wage[j];
            }
        }
        if(Next == T) break;//找不到点 图本身不连通
        vis[Next] = true;//标记 已经并入集合
        Mincut = Max;//每次更新
        S = T, T = Next;// 记录前、后点
        for(int j = 1; j <= N; j++)//继续找不在集合 且 没有被合并过的点
        {
            if(In[j] || vis[j]) continue;
            wage[j] += Map[Next][j];//累加 连通度
        }
    }
    return Mincut;
}
int Stoer_wagner()
{
    memset(In, false, sizeof(In));
    int ans = INF;
    for(int i = 0; i < N-1; i++)
    {
        ans = min(ans, work());
        if(ans == 0) return 0;//本身不连通
        In[T] = true;
        for(int j = 1; j <= N; j++)//把T点合并到S点
        {
            if(In[j]) continue;//已经合并
            Map[S][j] += Map[T][j];
            Map[j][S] += Map[j][T];
        }
    }
    return ans;
}
int main()
{
    while(scanf("%d%d", &N, &M) != EOF)
    {
        getMap();
        printf("%d\n", Stoer_wagner());
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值