二分图最大权匹配算法KM

有个博客理解起来很棒,描述的生动形象,插眼:https://blog.csdn.net/chenshibo17/article/details/79933191

用处:给定一张二分图,二分图的每一条边都带有一个权值,求出该二分图的一组最大匹配,使得匹配边的权值总和最大,注意,二分图带权最大匹配的前提是匹配数最大,然后再最大化匹配边的权值总和

局限性:只能满足“带权最大匹配一定是完备匹配”的图中正确求解,完备匹配就是给定一张二分图,其左部、右部节点数相同,均为N个节点。如果该二分图的最大匹配包含N条匹配边,则称该二分图具有完备匹配

时间复杂度:N^4,随机数据N^3

注意:我现在的理解是因为KM的局限性太大,并且时间复杂度也不太优秀,基本上所有可以用KM解决的问题都可以用费用流来解决,所以仅了解一下KM的定义和模板,后续会继续学习网络流

代码:

const int N=310;

int n;

int la[N],lb[N];//顶标

bool visa[N],visb[N];//访问标记

int maze[N][N];//边权

int match[N];//右部点匹配了哪一个左部点

int upd[N];

bool dfs(int x)
{
	visa[x]=true;//访问标记:x在交错树中
	for(int i=1;i<=n;i++)
	{
		if(!visb[i])
		{
			if(la[x]+lb[i]-maze[x][i]==0)//相等子图
			{
				visb[i]=true;//访问标记:y在交错树中
				if(!match[i]||dfs(match[i]))
				{
					match[i]=x;
					return true;
				}
			}
			else
				upd[i]=min(upd[i],la[x]+lb[i]-maze[x][i]);
		}
	}
	return false;
} 

int KM()
{
	memset(match,0,sizeof(match));
	for(int i=1;i<=n;i++)
	{
		la[i]=-inf;
		lb[i]=0;
		for(int j=1;j<=n;j++)
			la[i]=max(la[i],maze[i][j]);
	}
	for(int i=1;i<=n;i++)
	{
		while(1)//直到左部点找到匹配
		{
			memset(visa,false,sizeof(visa));
			memset(visb,false,sizeof(visb));
			memset(upd,inf,sizeof(upd));
			if(dfs(i))
				break;
			int delta=inf;
			for(int j=1;j<=n;j++)
				if(!visb[j])
					delta=min(delta,upd[j]);
			for(int j=1;j<=n;j++)//修改顶标
			{
				if(visa[j])
					la[j]-=delta;
				if(visb[j])
					lb[j]+=delta;
			}
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++)
		ans+=maze[match[i]][i];
	return ans;
}

 

你可能感兴趣的:(图论,KM算法,二分图最大权匹配)