#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 500 + 10;
const int INF = 1000000000;
int W[maxn][maxn], n;
int Lx[maxn], Ly[maxn]; // 顶标
int left[maxn]; // left[i]为右边第i个点的匹配点编号
bool S[maxn], T[maxn]; // S[i]和T[i]为左/右第i个点是否已标记
bool match(int i){
S[i] = true;
for(int j = 1; j <= n; j++) if (Lx[i]+Ly[j] == W[i][j] && !T[j]){
T[j] = true;
if (!left[j] || match(left[j])){
left[j] = i;
return true;
}
}
return false;
}
void update(){
int a = INF;
for(int i = 1; i <= n; i++) if(S[i])
for(int j = 1; j <= n; j++) if(!T[j])
a = min(a, Lx[i]+Ly[j] - W[i][j]);
for(int i = 1; i <= n; i++) {
if(S[i]) Lx[i] -= a;
if(T[i]) Ly[i] += a;
}
}
void KM() {
for(int i = 1; i <= n; i++) {
left[i] = Lx[i] = Ly[i] = 0;
for(int j = 1; j <= n; j++)
Lx[i] = max(Lx[i], W[i][j]);
}
for(int i = 1; i <= n; i++) {
for(;;) {
for(int j = 1; j <= n; j++) S[j] = T[j] = false;
if(match(i)) break; else update();
}
}
}
上面模版是对完全图;
下面的是对需要构边的图,用邻接表存
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 500 + 5; // 顶点的最大数目
const int INF = 1000000000;
// 最大权匹配
struct KM {
int n; // 左右顶点个数
vector<int> G[maxn]; // 邻接表
int W[maxn][maxn]; // 权值
int Lx[maxn], Ly[maxn]; // 顶标
int left[maxn]; // left[i]为右边第i个点的匹配点编号,-1表示不存在
bool S[maxn], T[maxn]; // S[i]和T[i]为左/右第i个点是否已标记
void init(int n) {
this->n = n;
for(int i = 0; i < n; i++) G[i].clear();
memset(W, 0, sizeof(W));
}
void AddEdge(int u, int v, int w) {
G[u].push_back(v);
W[u][v] = w;
}
bool match(int u){
S[u] = true;
for(int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if (Lx[u]+Ly[v] == W[u][v] && !T[v]){
T[v] = true;
if (left[v] == -1 || match(left[v])){
left[v] = u;
return true;
}
}
}
return false;
}
void update(){
int a = INF;
for(int u = 0; u < n; u++) if(S[u])
for(int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if(!T[v]) a = min(a, Lx[u]+Ly[v] - W[u][v]);
}
for(int i = 0; i < n; i++) {
if(S[i]) Lx[i] -= a;
if(T[i]) Ly[i] += a;
}
}
void solve() {
for(int i = 0; i < n; i++) {
Lx[i] = *max_element(W[i], W[i]+n);
left[i] = -1;
Ly[i] = 0;
}
for(int u = 0; u < n; u++) {
for(;;) {
for(int i = 0; i < n; i++) S[i] = T[i] = false;
if(match(u)) break; else update();
}
}
}
};
KM solver;
O(n4)的复杂度,使用时没有理论上那么糟,编号得从1开始,若最优匹配的前提最大匹配不满足,会死循环。