匈牙利算法

问题

解决二分图最大匹配问题。

思想

不断寻找原有匹配 \(M\) 的增广路径以增大匹配数。
对每次匹配左边点 \(x\),不断通过右边点 \(y\) 跳到左边 \(y\) 的对象点 \(x*\),直至 \(x\) 右边点 \(y\) 没有对象。

代码

模板

#include<bits/stdc++.h>
#define pb push_back
using namespace std;
const int N=509;
int n,m,e,ans;
int vis[N],t[N];
vector<int>v[N];
bool dfs(int x)
{
	for(auto y:v[x])
		if(!vis[y])
		{
			vis[y]=1;
			if(!t[y]||dfs(t[y]))
			{
				t[y]=x;
				return 1;
			}
		}
	return 0;
}
int main()
{
	cin>>n>>m>>e;
	for(int i=1;i<=e;i++)
	{
		int x,y;
		cin>>x>>y;
		if(x>n||y>m) continue;
		v[x].pb(y);
	}
	for(int i=1;i<=n;i++)
	{
		memset(vis,0,sizeof(vis));
		if(dfs(i)) ans++;
	}
	cout<<ans<<endl;
	return 0;
}

KM算法

问题

求二分图最大权完美匹配。

步骤

  1. 初始化可行顶标的值 (设定 \(lx,ly\) 的初始值)
    \(lx_i=max(w[i][j])\)\(ly_i=0\)

  2. 用匈牙利算法寻找相等子图的完备匹配
    完备匹配满足 \(lx[x]+ly_[y]=w[x][y]\)

  3. 若未找到增广路则修改可行顶标的值
    对路径上的左节点 \(lx[i]-=minn\) ,对路径上的右节点 \(ly[i]+=minn\)
    \(minn\) 为路径上左节点与路径上右节点 \(lx[x]+ly_[y]-w[x][y]\) 的最小值

  4. 重复(2)(3)直到找到相等子图的完备匹配为止

个人理解

初始顶标和可能是最大权,在之后每次匹配下会不断发生冲突,因此需解决在已有匹配下调整匹配使得匹配最优,所用思想即为贪心

每次匹配下,若有冲突,路径上左节点有 \(m\) 个,右节点 \(m-1\) 个,最优解决方法即为:对路径上 \(m\) 个左节点 \(-minn\) ,对路径上 \(m-1\) 个右节点 \(+minn\)

这样,相当于左顶标流向右顶标,保证了路径上 \(lx[x]+ly_[y]=w[x][y]\) ,也使得路径上某个左节点(取 \(minn\) 的节点)可以与路径上右节点(取 \(minn\) )匹配。同时,总的顶标和仅减小一个 \(minn\),权值也最优。

博文推荐

KM算法详解+模板 -本文没有给出KM算法的原理,只是模拟了一遍算法的过程。明白易懂算法流程
二分图的最佳完美匹配——KM算法 -详细证明

代码

题目

#include<bits/stdc++.h>
using namespace std;
const int N=29;
int n,minn,ans;
int p[N][N],q[N][N],lx[N],ly[N],visx[N],visy[N],t[N];
bool dfs(int x)
{
	visx[x]=1;
	for(int y=1;y<=n;y++)
		if(!visy[y])
		{
			int temp=lx[x]+ly[y]-p[x][y];
			if(!temp)
			{
				visy[y]=1;
				if(!t[y]||dfs(t[y]))
				{
					t[y]=x;
					return true;
				}
			}
			else minn=min(minn,temp);
		}
	return false;
}
void KM()
{
	for(int i=1;i<=n;i++)
		while(1)
		{
			minn=1e9+7;
			memset(visx,0,sizeof(visx));
			memset(visy,0,sizeof(visy));
			if(dfs(i)) break;
			for(int j=1;j<=n;j++)
			{
				if(visx[j]) lx[j]-=minn;
				if(visy[j]) ly[j]+=minn;
			}
		}
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			cin>>p[i][j];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			cin>>q[i][j];
			p[j][i]*=q[i][j];
			lx[j]=max(lx[j],p[j][i]);
		}
	KM();
	for(int i=1;i<=n;i++)
		ans+=p[t[i]][i];
	cout<<ans<<endl;
	return 0;
}
posted on 2021-11-12 19:41  YL-9  阅读(209)  评论(0)    收藏  举报