KM算法

124 篇文章 0 订阅

极其极其强烈推荐阅读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]);
	}
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值