2013-team4/code/stoer-wagner

从 Trac 迁移的文章

这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。

原文章内容如下:

{{{
给出一个无向图,求移除权值最少的边使得这个图被分为两个连通块。
Stoer-Wagner算法:时间复杂度O(N^3),空间复杂度O(N^2)
算法步骤:利用了Prim的贪心思想,每次加入最长路径的点,我们定义W(A, p)为A中的所有点到集合A外一点p的权值的和,设无向图为G(V,E):
①初始化最小割min_cut=+∞;
②集合A={a},a为V中任意顶点;
③更新W(A,x),选取V-A中的W(A, x)最大的点x加入集合A,重复前面直到|A|=|V|。设倒数第二个加入A的点为s,添加s后的集合A设为集合B,最后一个加入A的点为t;
④更新min_cut=min(min_cut,W(B,t));
⑤新建顶点c, 添加到V,权值w(c,v)=w(v,c)=w(s,v)+w(t,v), 删除顶点s和t, 以及与这两点相连的边;
⑥如果此时|V|>1则转②;
}}}
{{{
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>

using namespace std;

const int MAXN=500+10;
const int inf=1000000000;

int map[MAXN][MAXN], dis[MAXN], ver[MAXN];
bool vis[MAXN];
int n, m;

int stoer_wagner(int n) {
    int i, j, ret=inf;
    for (i=0; i<n; i++) ver[i]=i;
    while (n>1) {
        int t=1, s=0;
        for (i=1; i<n; i++) {
            dis[ver[i]]=map[ver[0]][ver[i]];
            if (dis[ver[i]]>dis[ver[t]]) t=i;
        }
        memset(vis, 0, sizeof(vis)); vis[0]=true;
        for (i=1; i<n; i++) {
            if (i==n-1) {
                ret=min(ret, dis[ver[t]]);
                for (j=0; j<n; j++) {
                    map[ver[s]][ver[j]]+=map[ver[j]][ver[t]];
                    map[ver[j]][ver[s]]+=map[ver[j]][ver[t]];
                }
                ver[t]=ver[--n];
            }
            vis[ver[t]]=true; s=t; t=-1;
            for (j=1; j<n; j++)
                if (!vis[ver[j]]) {
                    dis[ver[j]]+=map[ver[s]][ver[j]];
                    if (t==-1||dis[ver[t]]<dis[ver[j]]) t=j;
                }
        }
    }
    return ret;
}

int main() {
    while (scanf("%d%d", &n, &m)==2) {
        memset(map, 0, sizeof(map));
        for (int u, v, w, i=0; i<m; i++) {
            scanf("%d%d%d", &u, &v, &w);
            map[u][v]+=w; map[v][u]+=w;
        }
        printf("%d\n", stoer_wagner(n));
    }
    return 0;
}
}}}
给出一个无向图,求移除权值最少的边使得这个图被分为两个连通块。
Stoer-Wagner算法:时间复杂度O(N^3),空间复杂度O(N^2)
算法步骤:利用了Prim的贪心思想,每次加入最长路径的点,我们定义W(A, p)为A中的所有点到集合A外一点p的权值的和,设无向图为G(V,E):
①初始化最小割min_cut=+∞;
②集合A={a},a为V中任意顶点;
③更新W(A,x),选取V-A中的W(A, x)最大的点x加入集合A,重复前面直到|A|=|V|。设倒数第二个加入A的点为s,添加s后的集合A设为集合B,最后一个加入A的点为t;
④更新min_cut=min(min_cut,W(B,t));
⑤新建顶点c, 添加到V,权值w(c,v)=w(v,c)=w(s,v)+w(t,v), 删除顶点s和t, 以及与这两点相连的边;
⑥如果此时|V|>1则转②;
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN=500+10;
const int inf=1000000000;
int map[MAXN][MAXN], dis[MAXN], ver[MAXN];
bool vis[MAXN];
int n, m;
int stoer_wagner(int n) {
    int i, j, ret=inf;
    for (i=0; i<n; i++) ver[i]=i;
    while (n>1) {
        int t=1, s=0;
        for (i=1; i<n; i++) {
            dis[ver[i]]=map[ver[0]][ver[i]];
            if (dis[ver[i]]>dis[ver[t]]) t=i;
        }
        memset(vis, 0, sizeof(vis)); vis[0]=true;
        for (i=1; i<n; i++) {
            if (i==n-1) {
                ret=min(ret, dis[ver[t]]);
                for (j=0; j<n; j++) {
                    map[ver[s]][ver[j]]+=map[ver[j]][ver[t]];
                    map[ver[j]][ver[s]]+=map[ver[j]][ver[t]];
                }
                ver[t]=ver[--n];
            }
            vis[ver[t]]=true; s=t; t=-1;
            for (j=1; j<n; j++)
                if (!vis[ver[j]]) {
                    dis[ver[j]]+=map[ver[s]][ver[j]];
                    if (t==-1||dis[ver[t]]<dis[ver[j]]) t=j;
                }
        }
    }
    return ret;
}
int main() {
    while (scanf("%d%d", &n, &m)==2) {
        memset(map, 0, sizeof(map));
        for (int u, v, w, i=0; i<m; i++) {
            scanf("%d%d%d", &u, &v, &w);
            map[u][v]+=w; map[v][u]+=w;
        }
        printf("%d\n", stoer_wagner(n));
    }
    return 0;
}