HDU3488 Tour(二分图最小完备匹配,KM算法,转化思想)

Problem Description

In the kingdom of Henryy, there are N (2 <= N <= 200) cities, with M
(M <= 30000) one-way roads connecting them. You are lucky enough to
have a chance to have a tour in the kingdom. The route should be
designed as: The route should contain one or more loops. (A loop is a
route like: A->B->……->P->A.) Every city should be just in one route. A
loop should have at least two cities. In one route, each city should
be visited just once. (The only exception is that the first and the
last city should be the same and this city is visited twice.) The
total distance the N roads you have chosen should be minimized.

Input

An integer T in the first line indicates the number of the test cases.
In each test case, the first line contains two integers N and M,
indicating the number of the cities and the one-way roads. Then M
lines followed, each line has three integers U, V and W (0 < W <=
10000), indicating that there is a road from U to V, with the distance
of W. It is guaranteed that at least one valid arrangement of the tour
is existed. A blank line is followed after each test case.

Output

For each test case, output a line with exactly one integer, which is
the minimum total distance.

Sample Input

1
6 9
1 2 5
2 3 5
3 1 10
3 4 12
4 1 8
4 6 11
5 4 7
5 6 9
6 5 4

Sample Output

42

思路

给你一个图有n个点,m条边,每个边有对应的权值
题目要求若干个环包含所有的顶点,并且每个顶点只出现一次(除了一个环中的起始点)使得环中所有边得权值之和最小

因为每个顶点只出现一次,那么每个顶点只关联两个顶点入度顶点和出度顶点,所以构造二分图,将一个点u拆成u,u’。那么对于这个二分图如果存在着完美匹配的话,那么原图中一定存在若干个环,环中包含每个顶点,对于权值之和最小,只需求最小权匹配即可

在使用KM算法的时候一定要记得把边权先全部变成-inf,然后如果求最小匹配,只需要将权值取相反数,结果取相反数

代码

#include <cstdio>
#include <cstring>
#include <cctype>
#include <string>
#include <set>
#include <iostream>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
#define mem(a,b) memset(a,b,sizeof(a))
#define inf 0x3f3f3f3f
using namespace std;
const int N=200+20;
int nx,ny;//两边的点数
int e[N][N];
int match[N],cx[N],cy[N];
int slack[N];
bool visx[N],visy[N];
bool dfs(int x)
{
    visx[x]=true;
    for(int y=0; y<ny; y++)
    {
        if(visy[y])continue;
        int tmp=cx[x]+cy[y]-e[x][y];
        if(tmp==0)
        {
            visy[y]=true;
            if(match[y]==-1||dfs(match[y]))
            {
                match[y]=x;
                return true;
            }
        }
        else if(slack[y]>tmp)
            slack[y]=tmp;
    }
    return false;
}
int KM()
{
    mem(match,-1);
    mem(cy,0);
    for(int i=0; i<nx; i++)
    {
        cx[i]=-inf;
        for(int j=0; j<ny; j++)
            if(e[i][j]>cx[i])
                cx[i]=e[i][j];
    }
    for(int x=0; x<nx; x++)
    {
        mem(slack,inf);
        while(true)
        {
            mem(visx,false);
            mem(visy,false);
            if(dfs(x))break;
            //不存在的时候就要开始加上边
            int d=inf;
            for(int i=0; i<ny; i++)
                if(!visy[i]&&d>slack[i])
                    d=slack[i];//找到最小的d
            for(int i=0; i<nx; i++)
                if(visx[i])
                    cx[i]-=d;
            for(int i=0; i<ny; i++)
            {
                if(visy[i]) cy[i]+=d;
                else slack[i]-=d;
            }
        }
    }
    int res=0;
    for(int i=0; i<ny; i++)
        if(match[i]!=-1)
            res+=e[match[i]][i];
    return res;
}
int main()
{
    int n,m,t,a,b,c;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        mem(e,-inf);
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            e[a-1][b-1]=max(e[a-1][b-1],-c);
        }
        nx=ny=n;
        printf("%d\n",-KM());
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值