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;
}