HDU - 3488(二分图带权匹配)

本文深入探讨了一种解决特定图论问题的算法——最小权环覆盖算法,旨在寻找覆盖所有点的多个环,使总边权和最小。通过拆分节点、构建带权二分图并采用Kuhn-Munkres算法,文章详细解释了如何处理含有重边的复杂图结构,最终实现高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:Q - Tour HDU - 3488
题意:找出若干个环覆盖所有点,并且总边权和最小。
节点个数 200 200 200,边数 30000 30000 30000,可能含有重边。

因为要找若干个不相交的环,而且题目保证一定有解,故说明最后的选择中一定是每个点被出,入一次,故可将一个点拆为入点和出点,出点在左侧,入点在右侧,若没有边权的限制就是二分图匹配了,加上边权的限制就是带权二分图匹配,因为要求最小带权,故需要将边权取反,求负权边的最小匹配。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn=309;
int w[maxn][maxn];
int la[maxn],lb[maxn];
bool va[maxn],vb[maxn];
int match[maxn];
int n,delta,upd[maxn];

bool dfs(int x){
    va[x]=1;
    for(int y=1;y<=n;++y)
        if(!vb[y])
            if(la[x]+lb[y]-w[x][y]==0){
                vb[y]=1;
                if(!match[y]||dfs(match[y])){
                    match[y]=x;
                    return 1;
                }
            }
            else upd[y]=min(upd[y],la[x]+lb[y]-w[x][y]);
    return 0;
}

int KM(){
    delta=0;
    for(int i=1;i<=n;++i){
        la[i]=-(1<<30);
        lb[i]=0;
        match[i]=0;
        for(int j=1;j<=n;++j)
            la[i]=max(la[i],w[i][j]);
    }
    for(int i=1;i<=n;++i)
        while(1){
            memset(va,0,sizeof(va));
            memset(vb,0,sizeof(vb));
            delta=1<<30;
            for(int j=1;j<=n;++j) upd[j]=1e9;
            if(dfs(i)) break;
            for(int j=1;j<=n;++j)
                if(!vb[j]) delta=min(delta,upd[j]);
            for(int j=1;j<=n;++j){
                if(va[j]) la[j]-=delta;
                if(vb[j]) lb[j]+=delta;
            }
        }
    int ans=0;
    for(int i=1;i<=n;++i) ans+=w[match[i]][i];
    return ans;
}
const int inf=0x3f3f3f3f;
int main(){
    int t,m,u,v,W;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n;++j) w[i][j]=-inf;
        while(m--){
            scanf("%d%d%d",&u,&v,&W);
            w[u][v]=max(w[u][v],-W);
        }
        printf("%d\n",-KM());
    }


    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值