原题:http://acm.hdu.edu.cn/showproblem.php?pid=2255
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define inf 1e9
using namespace std;
const int maxn = 310;
int nx, ny; //nx、ny分别表示X集和Y集的顶点数;
int match[maxn];
int lx[maxn], ly[maxn], slack[maxn], w[maxn][maxn]; //lx、ly分别为X集和Y集的顶标;
bool visx[maxn], visy[maxn];
bool dfs(int x)
{
visx[x] = true;
for(int y = 1;y<=ny;y++)
{
if(visy[y]) continue;
int t = lx[x]+ly[y]-w[x][y];
if(t == 0)
{
visy[y] = true;
if(match[y] == 0 || dfs(match[y]))
{
match[y] = x;
return true;
}
}
else if(slack[y]>t)
slack[y] = t;
}
return false;
}
int KM()
{
memset(ly, 0, sizeof ly);
memset(match, 0, sizeof match);
for(int i = 1;i<=nx;i++) //将lx初始化为与它关联的边中权值最大的;
{
lx[i] = -inf;
for(int j = 1;j<=ny;j++)
lx[i] = max(lx[i], w[i][j]);
}
for(int x = 1;x<=nx;x++)
{
for(int y = 1;y<=ny;y++)
slack[y] = inf;
while(1)
{
memset(visx, false, sizeof visx);
memset(visy, false, sizeof visy);
if(dfs(x)) //如果成功,则表示找到了增广轨,该点增广完成,就可以寻找下一个点的增广轨;
break; //如果失败,则需要改变一些点的标号,增加可行边;
int d = inf;
for(int i = 1;i<=ny;i++)
{
if(!visy[i])
d = min(d, slack[i]);
}
for(int i = 1;i<=nx;i++)
{
if(visx[i])
lx[i]-=d;
}
for(int i = 1;i<=ny;i++)
{
if(visy[i])
ly[i]+=d;
else
slack[i]-=d;
}
}
}
int res = 0;
for(int i = 1;i<=ny;i++)
{
if(match[i])
res+=w[match[i]][i];
}
return res;
}
int main()
{
int n;
while(scanf("%d", &n)!=EOF)
{
nx = ny = n;
for(int i = 1;i<=n;i++)
for(int j = 1;j<=n;j++)
scanf("%d", &w[i][j]);
int ans = KM();
printf("%d\n", ans);
}
return 0;
}