hdu3691Nubulsa Expo(Stoer-Wagner求无向图全局最小割)

题目请戳这里

题目大意:给一张图,n个点,m条无向边,每条边有权值,表示该路人流量上界。给定起点s,问如何选终点t,能是s-t的所有路径上最小人流量总和最大,给出这个最大流量。

题目分析:根据最大流最小割定理,此题就是求一个最小割。给定的起点是无用信息,因为起点一定在某个割集中,那么终点在另一个割集随便找一点即可。所以此题求的是一个全局最小割。最大流可以解决。但需要O(n)枚举终点。再加上最大流的复杂度,至少要O(n^4),对于此题来说复杂度偏高,所以要找其他算法。

Stoer-Wagner算法是求无向图全局最小割的一个有效算法,最坏时间复杂度O(n^3),主要思想是先找任意2点的最小割,然后记录下这个最小割,再合并这2个点。这样经过n-1次寻找任意2点最小割,每次更新全局最小割,最后整张图缩成一个点,算法结束,所保存下来的最小割就是全局最小割。

Stoer-Wagner的正确性:

设s和t是图G的2个顶点,图G的全局最小割要么是s-t的最小割,此时s和t在G的全局最小割的2个不同的子集中,或者是G中将s和t合并得的的新图G'的全局最小割,此时s和t在G的全局最小割的同一个子集中。所以只需要不断求出当前图中任意2个点的最小割,然后合并这2个点。不断缩小图的规模求得最小割。

关于更详细的Stoer-Wagner算法:

1:这是英文版论文,英语太烂,没勇气看,不过里面有个插图蛮好的,可以很直观的体会这个算法的工作过程。

2:这篇给了一点证明

3:看看吧

详情请见代码:

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 305;
const int M = 50005;
const int inf = 0x3f3f3f3f;
int g[N][N],v[N],dis[N];
bool vis[N];
int m,n,s;
void build()
{
    int a,b,c;
    memset(g,0,sizeof(g));
    while(m --)
    {
        scanf("%d%d%d",&a,&b,&c);
        g[a][b] += c;
        g[b][a] += c;
    }
}
void solve()
{
    int i,j;
    int ans = inf;
    int maxx,maxi;
    int s,t;
    for(i = 1;i <= n;i ++)
        v[i] = i;//初始化点集
    while(n > 1)
    {
        int cur,pre;
        cur = 1;
        memset(dis,0,sizeof(dis));
        memset(vis,false,sizeof(vis));
        for(i = 2;i <= n;i ++)
        {
            dis[v[i]] = g[v[1]][v[i]];
        }
        vis[v[1]] = true;
        for(i = 1;i < n;i ++)
        {
            maxx = -1;
            maxi = 0;
            for(j = 1;j <= n;j ++)
            {
                if(vis[v[j]] == false && maxx < dis[v[j]])
                {
                    maxx = dis[v[j]];//找离当前集合最远的点
                    maxi = j;
                }
            }
            vis[v[maxi]] = true;
            if(i == n - 2)
                s = maxi;
            if(i == n - 1)
                t = maxi;
            for(j = 1;j <= n;j ++)
            {
                if(vis[v[j]] == false)
                    dis[v[j]] += g[v[maxi]][v[j]];
            }
        }
        ans = min(ans,dis[v[t]]);
        for(i = 1;i <= n;i ++)
        {
            g[v[s]][v[i]] += g[v[t]][v[i]];
            g[v[i]][v[s]] = g[v[s]][v[i]];
        }
        v[maxi] = v[n];
        n --;
    }
    printf("%d\n",ans);
}
int main()
{
    while(scanf("%d%d%d",&n,&m,&s),n)
    {
        build();
        solve();
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值