这个题要用KM算法来计算最大权值,但这里要算最小的,,则需要进行转化,
将map初始化到负无穷,然后map存负的权值,这样小的距离一变负的,就成大的了,就可以用,KM了,最后取负值便可以了。。
另外也可以用以大数减小变大数来利用KM计算。。
题意:给你一个有向图,边有权值,现在要你求若干个环包含所有的顶点,并且每个顶点只出现一次(除了一个环中的起始点)使得环中所有边得权值之和最小。
分析:因为每个顶点只出现一次,那么每个顶点只关联两个顶点入度顶点和出度顶点,所以构造二分图,将一个点u拆成u,u'。那么对于这个二分图如果存在着完美匹配的话,那么原图中一定存在若干个环,环中包含每个顶点,对于权值之和最小,只需求最小权匹配即可。
#include<stdio.h>
#include<string.h>
#define inf 9999999
int sx[1000],sy[1000];
int lx[1000],ly[1000];
int map[1000][1000];
int n,m,a,mark[1000],link[1000];
int find(int k)
{
int i;
sx[k]=1;
for(i=1;i<=n;i++)
{
if(sy[i]==0&&lx[k]+ly[i]==map[k][i])
{
sy[i]=1;
if(link[i]==-1||find(link[i]))
{
link[i]=k;
return 1;
}
}
}
return 0;
}
int KM()
{
int i,j,k,sum=0;
memset(lx,0,sizeof(lx));
memset(ly,0,sizeof(ly));
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(lx[i]<map[i][j])
lx[i]=map[i][j];
for(i=1;i<=n;i++)
{
while(1)
{
memset(sx,0,sizeof(sx));
memset(sy,0,sizeof(sy));
if(find(i))
break;
a=inf;
for(j=1;j<=n;j++)
{
if(sx[j])
{
for(k=1;k<=n;k++)
if(!sy[k])
{
if(a>lx[j]+ly[k]-map[j][k])
a=lx[j]+ly[k]-map[j][k];
}
}
}
for(j=1;j<=n;j++)
{
if(sx[j])
lx[j]-=a;
if(sy[j])
ly[j]+=a;
}
}
}
for(i=1;i<=n;i++)
sum+=map[link[i]][i];
return sum;
}
int main()
{
int i,j,a,b,c;
int k;
scanf("%d",&k);
while(k--)
{
scanf("%d%d",&n,&m);
memset(link,-1,sizeof(link));
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
map[i][j]=-inf;
for(i=0;i<m;i++)
{
scanf("%d%d%d",&a,&b,&c);
if(-c>map[a][b])
map[a][b]=-c;
}
printf("%d\n",-KM());
}
return 0;
}