hdu3488-Tour(最大权匹配变种)

题目来源:http://showproblem.php?pid=3488

题意

有一个有向图,图中包括一个或多个环,那么使用一个或者多个环的情况下将所有点进行覆盖,最小权值是多少。环:只有一个点经过两次,其余各点各经过一次

思路

Kuhn-Munkers算法的几种变形应用
1.Kuhn-Munkers算法是求最大权完备匹配,如果要求最小权完备匹配怎么办?方法很简单,只需将所有的边权值取其相反数,求最大权完备匹配,匹配的值再取相反数即可。
2.Kuhn-Munkers算法的运行要求是必须存在一个完备匹配,如果求一个最大权匹配(不一定完备)该如何办?依然很简单,把不存在的边权值赋为0。
3.Kuhn-Munkers算法求得的最大权匹配是边权值和最大,如果我想要边权之积最大,又怎样转化?还是不难办到,每条边权取自然对数,然后求最大和权匹配,求得的结果a再算出e^a就是最大积匹配。
以上文字来源:http://dsqiu.iteye.com/blog/1689505
目测这道题属于第一种变种。。。。
so 。。。模板躺过 。。。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=200+19;
const int INF=0x3f3f3f3f;
int link[maxn],v_x[maxn],v_y[maxn];
int mp[maxn][maxn],slack[maxn],ex_x[maxn],ex_y[maxn];
int n,w;
void init()
{
    scanf("%d%d",&n,&w);
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            mp[i][j]=-INF;
    for(int i=0; i<w; i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        if(mp[x][y]<-z)
            mp[x][y]=-z;
    }
}
bool dfs(int i)
{
    v_x[i]=1;
    for(int j=1; j<=n; j++)
    {
        if(!v_y[j])
        {
            int dx=ex_x[i]+ex_y[j]-mp[i][j];
            if(dx==0)
            {
                v_y[j]=1;
                if(link[j]==-1||dfs(link[j]))
                {
                    link[j]=i;
                    return true;
                }
            }
            else
                slack[j]=min(slack[j],dx);

        }
    }
    return false;
}

void solve()
{
    memset(link,-1,sizeof(link));
    memset(ex_y,0,sizeof(ex_y));
    for(int i=1; i<=n; i++)
    {
        ex_x[i]=-INF;//纠结了很久。。。
        for(int j=1; j<=n; j++)
            if(ex_x[i]<mp[i][j])
                ex_x[i]=mp[i][j];
    }
    for(int i=1; i<=n; i++)
    {
        memset(slack,INF,sizeof(slack));
        while(1)
        {
            memset(v_x,0,sizeof(v_x));
            memset(v_y,0,sizeof(v_y));
            if(dfs(i)) break;
            int d=INF;
            for(int j=1; j<=n; j++)
            {
                if(!v_y[j])
                    d=d>slack[j]?slack[j]:d;
            }
            for(int j=1; j<=n; j++)
            {
                if(v_x[j]) ex_x[j]-=d;
                if(v_y[j]) ex_y[j]+=d;
                else slack[j]-=d;//因为上一句。。。
            }
        }
    }
    int sum=0;
    for(int i=1; i<=n; i++)
        sum+=mp[link[i]][i];
    printf("%d\n",-sum);
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        init();
        solve();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值