注意KM算法 求解最大权值匹配时,边权赋值为正。求解最小权值匹配时,边权赋值为负,最后结果再取反。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 400
#define INF 100000000//注意INF值要比所有边权值大
using namespace std;
int match[MAXN];//匹配
int lx[MAXN], ly[MAXN];//顶标
int slack[MAXN];//记录T集中节点的松弛量
int Map[MAXN][MAXN];//存储原图S-T节点间的关系
bool visx[MAXN], visy[MAXN];
int nx, ny;//S集中节点数目 T集中节点数目
int DFS(int x)
{
visx[x] = true;
for(int y = 0; y < ny; y++)
{
if(visy[y]) continue;
int t = lx[x] + ly[y] - Map[x][y];//判断边<x,y>是否在相等子图里面
if(t == 0)//边<x,y>在相等子图里面
{
visy[y] = true;
if(match[y] == -1 || DFS(match[y]))
{
match[y] = x;
return 1;
}
}
else if(slack[y] > t)// 更新松弛量
slack[y] = t;
}
return 0;
}
int KM()
{
memset(match, -1, sizeof(match));
memset(ly, 0, sizeof(ly));
for(int x = 0; x < nx; x++)//lx[x]初始化为Map[x][y]中最大值
{
lx[x] = -INF;
for(int y = 0; y < ny; y++)
{
if(Map[x][y] > lx[x])
lx[x] = Map[x][y];
}
}
for(int x = 0; x < nx; x++)//匹配
{
for(int i = 0; i < ny; i++)//初始化T集中所有节点的 松弛量
slack[i] = INF;
while(1)
{
memset(visx, false, sizeof(visx));
memset(visy, false, sizeof(visy));
if(DFS(x)) break;//匹配成功
//匹配失败
int d = INF;
//寻找最小的松弛量d
for(int i = 0; i < ny; i++)
{
if(!visy[i] && d > slack[i])
d = slack[i];
}
//修改顶标 为了有更大机会完美匹配
//S集里面所有点 全部减去最小松弛量d
for(int i = 0; i < nx; i++)
{
if(visx[i])
lx[i] -= d;
}
//为了保证S-T的匹配不离开相等子图即对边<x,y>依旧有lx[x] + ly[y] == Map[x][y]
//T集里面所有点 全部增加最小松弛量d
for(int i = 0; i < ny; i++)
{
if(visy[i]
ly[i] += d;
else//节点的松弛量 减少d
slack[i] -= d;
}
}
}
int res = 0;
for(int i = 0; i < ny; i++)
{
if(match[i] != -1)//有匹配
res += Map[match[i]][i];//累加权值
}
return res;
}
int main()
{
int N;
while(scanf("%d", &N) != EOF)
{
nx = ny = N;
for(int i = 0; i < N; i++)
for(int j = 0; j < N; j++)
scanf("%d", &Map[i][j]);//第i个和第j个匹配的权值
int ans = KM();
printf("%d\n", ans);
}
return 0;
}