初始化可行顶标的值
用匈牙利算法寻找相等子图的完备匹配
若未找到增广路则修改可行顶标的值
重复上述过程直到找到相等子图的完备匹配为止
/*
km算法
二分图最大权匹配
复杂度:O(V*E)
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define INF 0x3fffffff
int g[500][500]; //给定的二分图
int ex1[500]; //左边的期望
int ex2[500]; //右边的期望
bool vis1[500]; //左边是否被访问过
bool vis2[500]; //右边是否被访问过
int match[500]; //记录右边匹配到的编号
int slack[500]; //在每一轮匹配中记录右边能被左边匹配到最少还差多少期望
int n,m; //n为二分图左边的数量,m为右边的数量
bool dfs(int x) //为编号为x的尝试匹配
{
vis1[x] = true;
for (int i = 1; i <= m; i++)
{
if( g[x][i] == 0 ) continue; //如果x与i没有边
if( vis2[i] ) continue; //每一轮匹配右边只能被尝试一次
int gap = ex1[x] + ex2[i] - g[x][i];
if( gap == 0 ) //期望相加等于权值,i点可以尝试进行匹配
{
vis2[i] = true;
if( match[i] == -1 || dfs(match[i]) )
//如果该点没有匹配或该点匹配的点可以匹配新的点
{
match[i] = x;
return true;
}
}else slack[i] = min(slack[i],gap);
//i点尝试匹配的资格都没有,更新距离成功被尝试匹配的期望差
}
return false;
}
int km() //求出二分图的最大权匹配
{
memset(match,-1,sizeof(match)); //初始match为-1
memset(ex2,0,sizeof(ex2)); //右边的期望为0
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
ex1[i] = max(ex1[i],g[i][j]); //左边的期望是与其相连的最大权值
}
}
for (int i = 1; i <= n; i++) //为左边的每个点进行分配
{
for (int j = 1; j <= m; j++)
{
slack[j] = INF;
}
while(1) //一轮匹配可能无法匹配到,所以是while
{
memset(vis1,0,sizeof(vis1));
memset(vis2,0,sizeof(vis2));
if( dfs(i) ) break; //找到就退出,找不到就要降低期望值
int d = INF; //最少可降低的期望值
for (int j = 1; j <= m; j++)
{
if( !vis2[j] ) d = min(d,slack[j]);
/*取这轮匹配中的没被尝试匹配过的slack最小值
左边期望减少这些,就可以保证可以尝试匹配新的点
*/
}
for (int j = 1; j <= n; j++)
{
if( vis1[j] ) ex1[j] -= d; //尝试匹配过的左边期望降低
}
for (int j = 1; j <= n; j++)
{
if( vis2[j] ) ex2[j] += d; //尝试匹配过的右边期望升高
else if( g[i][j] ) slack[j] -= d;
/*没有成功被尝试匹配的,并且与i有边相连
由于尝试匹配的左边的点期望减少,所以j最少需要的期望减少
*/
}
}
}
int ans = 0;
for (int i = 1; i <= m; i++)
{
ans += g[match[i]][i];
}
return ans;
}
int main()
{
while( scanf("%d",&n) != EOF )
{
m = n;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
scanf("%d",&g[i][j]);
}
}
printf("%d\n",km());
}
return 0;
}