#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=310,OO=INT_MAX;
int w[maxn][maxn];
int lx[maxn],ly[maxn];
int linky[maxn];
int visx[maxn],visy[maxn];
int N;
int slack[maxn];
void input(){
for(int i=0;i<N;++i)
for(int j=0;j<N;++j)
scanf("%d",&w[i][j]);
}
bool find(int x){
visx[x]=true;
for(int y=0;y<N;++y){
if(visy[y])continue;
int t=lx[x]+ly[y]-w[x][y];
if(t==0){
visy[y]=true;
if(linky[y]==-1||find(linky[y])){
linky[y]=x;
return true;
}
}
else{
if(slack[y]>t)
slack[y]=t;
}
}
return false;
}
void KM(){
memset(linky,-1,sizeof(linky));
memset(lx,0,sizeof(lx));
memset(ly,0,sizeof(ly));
for(int i=0;i<N;++i)
for(int j=0;j<N;++j)
if(w[i][j]>lx[i])
lx[i]=w[i][j];
for(int x=0;x<N;++x){
for(int i=0;i<N;++i)
slack[i]=OO;
for(;;){
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
if(find(x))break;
int d=OO;
for(int i=0;i<N;++i){
if(!visy[i])
if(d>slack[i])
d=slack[i];
}
for(int i=0;i<N;++i){
if(visx[i])
lx[i]-=d;
}
for(int i=0;i<N;++i){
if(visy[i])
ly[i]+=d;
else
slack[i]-=d;
}
}
}
}
void output(){
int res=0;
for(int j=0;j<N;++j){
//for(int i=0;i<N;++i)
//res+=w[i][j];
res+=w[linky[j]][j];
}
printf("%d\n",res);
}
int main(){
while (scanf("%d", &N) != EOF){
input();
KM();
output();
}
}
[KM算法的几种转化]
KM算法是求最大权完备匹配,如果要求最小权完备匹配怎么办?方法很简单,只需将所有的边权值取其相反数,求最大权完备匹配,匹配的值再取相反数即可。
KM算法的运行要求是必须存在一个完备匹配,如果求一个最大权匹配(不一定完备)该如何办?依然很简单,把不存在的边权值赋为0。
KM算法求得的最大权匹配是边权值和最大,如果我想要边权之积最大,又怎样转化?还是不难办到,每条边权取自然对数,然后求最大和权匹配,求得的结果a再算出e^a就是最大积匹配。至于精度问题则没有更好的办法了。