利用KM算法中的l(x)+l(y)>=w(x,y)。算法结束后所有标顶之和是最小的。即所求答案。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#include <algorithm>
#define ll long long
#define INF 2139062143
#define inf -2139062144
#define MOD 20071027
#define MAXN 505
using namespace std;
int link[MAXN],gl[MAXN][MAXN],lx[MAXN],ly[MAXN],slack[MAXN];
bool visx[MAXN],visy[MAXN];
int n;
bool match(int x)
{
visx[x]=true;
for(int y=1; y<=n; ++y)
if(!visy[y])
{
int t=lx[x]+ly[y]-gl[x][y];
if(t==0)
{
visy[y]=true;
if(link[y]==-1||match(link[y]))
{
link[y]=x;
return true;
}
}
else slack[y]=min(slack[y],t);
}
return false;
}
int KM()
{
memset(lx,0x80,sizeof(lx));
memset(ly,0,sizeof(ly));
memset(link,-1,sizeof(link));
for(int i=1; i<=n; ++i)
for(int j=1; j<=n; ++j)
lx[i]=max(lx[i],gl[i][j]);
for(int x=1; x<=n; ++x)
{
memset(slack,0x7f,sizeof(slack));
while(1)
{
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
if(match(x)) break;
int d=INF;
for(int i=1; i<=n; ++i)
if(!visy[i]&&d>slack[i])
d=slack[i];
for(int i=1; i<=n; ++i)
if(visx[i]) lx[i]-=d;
for(int i=1; i<=n; ++i)
if(visy[i]) ly[i]+=d;
else slack[i]-=d;
}
}
int res=0;
for(int i=1; i<=n; ++i)
if(link[i]!=-1)
res+=gl[link[i]][i];
return res;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i=1; i<=n; ++i)
for(int j=1; j<=n; ++j)
scanf("%d",&gl[i][j]);
int res=KM();
for(int i=1; i<=n; ++i)
if(i==1) printf("%d",lx[i]);
else printf(" %d",lx[i]);
printf("\n");
for(int i=1; i<=n; ++i)
if(i==1) printf("%d",ly[i]);
else printf(" %d",ly[i]);
printf("\n");
printf("%d\n",res);
}
return 0;
}