极其极其强烈推荐阅读2015国家队候选队论文《浅谈图的匹配算法及其应用》,
如果你是一位极其熟练的OI选手那么你是可以轻松的只用网费下载到PDF的
个人理解,不喜勿喷,欢迎推广。
KM算法的原理在于这样两个事实:
1.
最大匹配等于最小点覆盖(证明自行百度)
同理:
最大权匹配等于最小顶标和(证明自行百度)。
顶标就是对于x部的每个点i定义一个顶点标号dx[i],y部为dy[i]
满足对于每一条边(u , v): w(u , v) <= dx[u] + dy[v]
求Min(sigma(dx[i]) + sigma(dy[i]))
2.
如果存在一个完备匹配使得其中的每一条边(u , v)满足条件1: w(u , v) == dx[u] + dy[v]
那么此时取到最小顶标和。
然后我们来求最小顶标和。
一开始先初始化dx为极大值,dy为0
然后我们来以满足条件1的边做增广路(什么逻辑?),
然后,假定我们一定会增广失败,
增广失败的原因是最后一个x部的点没有邻接点,没有满足条件1 的边,
所以我们把x部的增广路径中的点顶标都减去一个d,
y部的增广路径中的点顶标都加上一个d,
使得满足条件1的边增加至少1条,
然后就可以继续增广。
对每一个点都增广一次。
那么就可以得到一个完备匹配满足条件1。
此时的最小顶标和就是最大权匹配。
附赠1kb精简代码一份
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#define maxn 405
using namespace std;
int n,dx[maxn],dy[maxn],s[maxn],cy[maxn],pre[maxn],a[maxn][maxn],tim;
int vis[maxn];
void bfs(int now)
{
int y=0,x,Minloc;
cy[y] = now;
memset(s,0x3f,sizeof s);
for(;cy[y];y=Minloc)
{
Minloc = 0;
x = cy[y] , vis[y] = tim;
for(int i=1;i<=n;i++)
if(vis[i]!=tim){
if(dx[x]+dy[i]-a[x][i]<s[i]) s[i]=dx[x]+dy[i]-a[x][i],pre[i]=y;
if(s[i]<s[Minloc]) Minloc = i;
}
for(int i=0,inc=s[Minloc];i<=n;i++)
if(vis[i]==tim) dx[cy[i]]-=inc,dy[i]+=inc;
else s[i]-=inc;
}
for(;y;y=pre[y]) cy[y] = cy[pre[y]];
}
int KM()
{
memset(cy,0,sizeof cy);
for(int i=1;i<=n;i++)
tim++,bfs(i);
int ans = 0;
for(int i=1;i<=n;i++) ans+=dx[i]+dy[i];
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
scanf("%d",&a[i][j]);
dx[i] = max(dx[i] , a[i][j]);
}
printf("%d\n",KM());
}
K
M
KM
KM算法感觉就是对偶之后在最大费用的条件下跑增广路的一种方式,
所以其实是可以求
K
K
K个匹配的最大权的。
C o d e \mathcal Code Code
#include<bits/stdc++.h>
#define maxn 1005
#define maxm maxn * maxn
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define LL long long
#define inf 0x3f3f3f3f
using namespace std;
namespace IO{
char cb[1<<16],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<16,stdin),cs==ct)?0:*cs++)
void read(int &res){
char ch;
for(;!isdigit(ch=getc()););
for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}
};
using IO :: read;
int n,Q;
int a[maxn][maxn];
int vis[maxn],avx[maxn],cy[maxn],pre[maxn],cx[maxn];
LL ans[maxn],s[maxn],dx[maxn],dy[maxn];
void aug(){
rep(i,0,n) avx[i] = 1 , vis[i] = 0 , s[i] = inf , pre[i] = 0;
// find the least slack in X
rep(i,1,n) avx[cy[i]] = 0;
int mnx = inf , u = 0;
rep(i,1,n) if(avx[i]){
int mn = inf;
rep(j,1,n){
int t = dx[i] + dy[j] - a[i][j];
mn = min(mn , t);
if(t < s[j]) s[j] = t , pre[j] = i;
}
if(mn < mnx) mnx = mn , cy[0] = i;
}
// find the augmentation road
for(int v , p , mn;cy[u];u=p){
mn = inf , v = cy[u];
vis[u] = 1 , avx[v] = 1;
rep(j,1,n)
if(!vis[j]){
int t = dx[v] + dy[j] - a[v][j];
if(t < s[j]) s[j] = t , pre[j] = v;
if(s[j] < mn) mn = s[j] , p = j;
}
rep(j,1,n){
if(avx[j]) dx[j] -= mn;
if(vis[j]) dy[j] += mn;
else s[j] -= mn;
}
}
for(int t = pre[u];u;t = pre[u]) cy[u] = t , swap(u , cx[t]);
}
int main(){
freopen("allocation.in","r",stdin);
freopen("allocation.out","w",stdout);
int Num;
read(Num);
read(n),read(Q);int x;
rep(i,1,n) rep(j,1,n) read(a[i][j]);
rep(i,1,n) dx[i] = inf;
rep(i,1,n){
aug();
rep(j,1,n) if(cy[j]) ans[i] += a[cy[j]][j];
}
for(int C;Q--;){
read(C);
C = min(C , n);
printf("%lld.0\n",ans[C]);
}
}