版权声明:转载注明下出处就行了。 https://blog.csdn.net/LJD201724114126/article/details/84029972
题目链接:hdu 1853
题意:给出n个点,m条边的带权有向图,让你分成几个循环走完全部点,求最小路径。
参考博客:https://blog.csdn.net/u013480600/article/details/38760767
把顶点i拆分成 i和i',
假设原图的有向环为(1->2->3->1) and(6->5->4->6),那么二分图的完备匹配就是1->2’ 2->3’ 3->1’6->5’ 5->4’ 4->6’
假设二分图的完备匹配是1->2’ 2->3’ 3->1’ 6->5’ 5->4’ 4->6’那么原图的有向环为(1->2->3->1) and (6->5->4->6))
///二分图拆点,KM算法
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define INF 0x3f3f3f3f
const int maxn=110;
int G[maxn][maxn],match[maxn],lx[maxn],ly[maxn],slack[maxn];
int nx,ny;
bool visx[maxn],visy[maxn];
bool findpath(int x)
{
int tempdelta;
visx[x]=1;
for(int y=0;y<ny;y++)
{
if(visy[y]) continue;
tempdelta=lx[x]+ly[y]-G[x][y];
if(tempdelta==0){
visy[y]=1;
if(match[y]==-1||findpath(match[y])){
match[y]=x;
return 1;
}
}
else if(slack[y]>tempdelta)
slack[y]=tempdelta;
}
return 0;
}
void KM()
{
for(int x=0;x<nx;x++)
{
for(int j=0;j<ny;j++) slack[j]=INF;
while(1)
{
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
if(findpath(x)) break;
int delta=INF;
for(int j=0;j<ny;j++)
if(!visy[j]) delta=min(delta,slack[j]);
for(int i=0;i<nx;i++)
if(visx[i]) lx[i]-=delta;
for(int j=0;j<ny;j++)
{
if(visy[j]) ly[j]+=delta;
else slack[j]-=delta;
}
}
}
}
void solve()
{
memset(match,-1,sizeof(match));
memset(ly,0,sizeof(ly));
for(int i=0;i<nx;i++)
{
lx[i]=-INF;
for(int j=0;j<ny;j++)
if(lx[i]<G[i][j]) lx[i]=G[i][j];
}
KM();
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
nx=ny=n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
G[i][j]=-INF;
while(m--)
{
int x,y,weight;
scanf("%d%d%d",&x,&y,&weight);///建图,拆点
if(-weight>G[x-1][y-1]) G[x-1][y-1]=-weight; }
solve();
int ans=0;
int num=0;
for(int j=0;j<ny;j++)
{
///此处的匹配一定不会为-1,因为把-INF当作权值了,故再加个判断就行了
if(match[j]==-1||G[match[j]][j]==-INF){
puts("-1");
break;
}
ans+=G[match[j]][j];
++num;
}
if(num==ny)
printf("%d\n",-ans);
}
return 0;
}