全局最小割模板

stoer-wagner算法:
//点下标从0开始
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;    
} 
堆优化stoer-wagner算法:
//复杂度(nmlogm),因此适合稀疏图,稠密图直接用没有堆优化的算法
//点下标从1开始
typedef pair<int, int> pii;
const int N = 3000 + 10, M = 20000 + 10, INF = 0x3f3f3f3f;
struct edge
{
    int to, cost, next;
}g[M];
int cnt, head[N], link[N];//link类似于链表
int par[N];
int dis[N];//dis数组用来表示该点与A集合中所有点之间的边的长度之和
bool vis[N];//用来标记是否该点加入了A集合  
void init(int n)
{
    for(int i = 1; i <= n; i++) par[i] = i;
    cnt = 0;
    memset(head, -1, sizeof head);
    memset(link, -1, sizeof link);
}
void add_edge(int v, int u, int cost)
{
    g[cnt].to = u, g[cnt].cost = cost, g[cnt].next = head[v], head[v] = cnt++;
}
int ser(int x)
{
    int r = x, i = x, j;
    while(r != par[r]) r = par[r];
    while(par[i] != r) j = par[i], par[i] = r, i = j;
    return r;
}
void unite(int x, int y)
{//把y合并到x中,反过来也对
    int p = x;
    while(~ link[p]) p = link[p];
    link[p] = y;
    par[y] = x;
}
int min_cut_phase(int n, int &s, int &t)
{
    memset(vis, 0, sizeof vis);
    memset(dis, 0, sizeof dis);
    priority_queue<pii> que;
    t = 1;
    while(--n)
    {
        vis[s = t] = true;
        for(int i = s; ~i; i = link[i])//更新dis数组,把合并到s中的点全部取出来
            for(int j = head[i]; ~j; j = g[j].next)
            {
                int v = ser(g[j].to);//g[j].to可能已经合并到其他点上了
                if(! vis[v]) que.push(make_pair(dis[v] += g[j].cost, v));
            }
        t = 0;
        while(! t)
        {
            if(que.empty()) return 0; //图不联通
            pii p = que.top(); que.pop();
            if(dis[p.second] == p.first) t = p.second;
        }
    }
    return dis[t];
}
int stoer_wagner(int n)
{
    int ans = INF, s, t;
    for(int i = n; i > 1; i--)
    {
        ans = min(ans, min_cut_phase(i, s, t));
        if(ans == 0) break;
        unite(s, t);
    }
    return ans;
}
int main()
{
    int n, m;
    while(~ scanf("%d%d", &n, &m))
    {
        init(n);
        int a, b, c;
        for(int i = 1; i <= m; i++)
        {
            scanf("%d%d%d", &a, &b, &c);
            add_edge(a, b, c), add_edge(b, a, c);
        }
        printf("%d\n", stoer_wagner(n));
    }
    return 0;
}
//别人的模板,没看懂,记录一下
#include <bits/stdc++.h>
//#include <ext/pb_ds/priority_queue.hpp>
#define rep(i,n) for(int i=1;i<=n;++i)
#define inf 0x3f3f3f3f
#define M 100005
#define N 3005
using namespace std;
//__gnu_pbds::priority_queue<int> q;

struct Eedge
{
    int x,y,w;
} e[M];
struct edge
{
    int v,c,f;
} ee[2][M];
struct node
{
    int len,pos;
};
int operator <(const node &a,const node &b)
{
    return a.len<b.len;
}
int cmp(Eedge x,Eedge y)
{
    if(x.x==y.x)return x.y<y.y;
    return x.x<y.x;
}
int b[2][N],d[N];
priority_queue<node> q;
int n,m,ans,x,y,z,tot[2],top,tp,ts,tr;
bool f[N],r;
int tab[N];
void adds(bool p,int x,int y,int w)
{
    ee[p][++tot[p]]=(edge)
    {
        y,w,b[p][x]
    };
    b[p][x]=tot[p];
}
void add(bool p,int x,int y,int w)
{
    adds(p,x,y,w);
    adds(p,y,x,w);
}
int main()
{
    while(scanf("%d%d",&n,&m)==2)
    {
        memset(b,0,sizeof b);
        tot[0]=tot[1]=0;
        ans=inf;
        rep(i,m)
        {
            scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].w);
            if(e[i].x>e[i].y)swap(e[i].x,e[i].y);
        }
        sort(e+1,e+m+1,cmp);
        top=0;
        rep(i,m)if(e[top].x!=e[i].x||e[top].y!=e[i].y)e[++top]=e[i];
        else e[top].w+=e[i].w;
        m=top;
        rep(i,m)if(e[i].x!=e[i].y)add(0,e[i].x,e[i].y,e[i].w);
        r=0;
        rep(zz,min(n-1,max(400,n/2-1)))
        {
            memset(d,0,sizeof d);
            memset(f,0,sizeof f);
            d[1]=inf;
            q.push((node)
            {
                d[1],1
            });
            tp=0;
            while(!q.empty())
            {
                x=q.top().pos;
                q.pop();
                if(f[x])continue;
                for(int i=b[r][x]; i; i=ee[r][i].f)
                {
                    int v=ee[r][i].v;
                    if(!f[v])d[v]+=ee[r][i].c,q.push((node){d[v],v});
                }
                f[x]=1;
                ++tp;
                if(tp==n-zz+1)ts=x;
                if(tp==n-zz)tr=x;
            }
            tp=0;
            for(int i=b[r][ts]; i; i=ee[r][i].f)tp+=ee[r][i].c;
            r^=1;
            ans=min(ans,tp);
            memset(b[r],0,sizeof b[r]);
            tot[r]=0;
            rep(i,n)if(i!=tr&&i!=ts)
            {
                for(int j=b[r^1][i]; j; j=ee[r^1][j].f)
                    if(ee[r^1][j].v!=tr&&ee[r^1][j].v!=ts)adds(r,i,ee[r^1][j].v,ee[r^1][j].c);
            }
            top=0;
            memset(tab,0,sizeof tab);
            for(int i=b[r^1][ts]; i; i=ee[r^1][i].f)if(ee[r^1][i].v!=tr)tab[ee[r^1][i].v]+=ee[r^1][i].c;
            for(int i=b[r^1][tr]; i; i=ee[r^1][i].f)if(ee[r^1][i].v!=ts)tab[ee[r^1][i].v]+=ee[r^1][i].c;
            rep(i,n)if(tab[i])add(r,ts,i,tab[i]);
        }
        printf("%d\n",ans);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值