阅读本文之前,首先假设您已经理解匈牙利算法,如果不理解友链在这
=>我是匈牙利算法<=
有时间会将匈牙利算法的博客补上的。
KM算法
KM算法求的是二分图完备匹配下的最大权匹配。
在二分图中,x点集中的所有点都有对应的匹配 且 y 点集中所有的点都有对应的匹配,则称该匹配为完备匹配。但是完备匹配可能不止一种,KM算法就是为了求出,其中边权和最大的一组。
KM算法的思路非常简单,核心可以看做这一句:
从最大权的匹配(不一定是完备匹配)开始,判断是否可以构成完备匹配。
如果可以则为答案,如果不可以则判断第二大、第三大、第四大......
直到找到答案或者没有更多匹配方案为止。
或许以上的说法还是有些迷糊的,那我们就看下面这一种更为清晰的说法。
首先找到从x中各个点出发的,各自的最大的一条边,构成匹配。
如果该匹配是完备匹配,则找到答案。
否则,加入所有未在该匹配中,最大的一条边,重新判断。
通过这样,我们可以保证,对于任意一个匹配,没有比当前匹配权值和更大的完备匹配。
算法的大致过程如下:
1.在原图中判断是否可以构成完备匹配,如果可以,步骤2。
2.向集合中加入从x中各个点出发的,各自的最大的一条边,构成匹配。
3.判断当且匹配是否为完备匹配,如果是,输出答案,否则,接步骤4
4.加入未在匹配中,且权值最大的一条边,重复3。
实现
显然,对于每条边进行标记是否在当且匹配中,是一个效率低下的方法。那么我们应该用什么样的方法来完成这一操作呢?
我们可以设置tagx[] tagy[] 这两个数组,对于x,y之间的边,一定满足
w[x][y]==tagx[x]+tagy[x];
也就是说,当且仅当以上条件成立时,我们才认为这是一条在匹配中的边。
初值:
假设从xi出发,距离最远的点为yi,那么tagx[xi]=w[xi][yi],tagy[yi]=0;
修改:
我们找到最小的 d = tagx[ i ]+tagy[ j ]-w[ i ][ j ] , 其中w[ i ][ j ]没有这条边没有被加入过,我们可以用两个marky[],markx[]数组判断这一点。
对于所有已经访问过的xi,yi,我们进行如下操作。
tagx[xi]-=d;
tagy[yi]+=d;
显然,对于已经在匹配中的一条边w[ xj ] [ yj ] 而言,由于d的增减可以通过tagx [ xj ] + tag[ yj ] 来抵消,所以并不影响。而对于新加入的这条边 w [ xj ][ yj ]来说, xj 被访问过,而yj 没有,所以tagx [ xj ] + tagy[ yj ] == w[ xj ][ yj ]
最后附上代码:
//KM.cpp
//
//Glasses
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define LL int
#define MAXN 300 +10
#define MOD 1000000007
#define INF 0x7f7f7f7f
#define inf 0x7f7f7f7f
#define FOR(i,a,b) for(LL i = (a), i##end = (b); i <= i##end; ++i)
#define ROF(i,a,b) for(LL i = (a), i##begin = (b); i >= i##begin; --i)
#define output(a) printf("%d\n",(a));
#define input(a) scanf("%d",&(a));
LL read()
{
LL x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();
return x;
}
struct KM
{
bool markx[MAXN],marky[MAXN];
LL tagx[MAXN],tagy[MAXN],match[MAXN],w[MAXN][MAXN];
LL n,m;
void init(LL inn,LL inm)
{
this->n=inn;this->m=inm;
FOR(i,0,MAXN-1)FOR(j,0,MAXN-1)w[i][j]=0;
}
void add_edge(LL x,LL y,LL z)
{
w[x][y]=max(w[x][y],z);
}
bool dfs(LL x)
{
markx[x]=1;
FOR(y,1,m)
if(!marky[y]&&tagx[x]+tagy[y]==w[x][y])
{
marky[y]=1;
if(!match[y]||dfs(match[y]))
{
match[y]=x;
return 1;
}
}
return 0;
}
LL solve()
{
memset(tagy,0,sizeof(tagy));
FOR(i,1,n)
{
tagx[i]=-INF;
FOR(j,1,m)tagx[i]=max(tagx[i],w[i][j]);
}
memset(match,0,sizeof(match));
FOR(k,1,n)
{
while(1)
{
memset(markx,0,sizeof(markx));
memset(marky,0,sizeof(marky));
if(dfs(k))break;
LL d=INF;
FOR(i,1,n)if(markx[i])
FOR(j,1,m)if(!marky[j])
d=min(d,tagx[i]+tagy[j]-w[i][j]);
if(d==INF)return -1;
FOR(i,1,n)if(markx[i])tagx[i]-=d;
FOR(i,1,m)if(marky[i])tagy[i]+=d;
}
}
LL sum=0;
FOR(i,1,m)if(match[i])sum+=w[match[i]][i];
return sum;
}
}e;
错误是我的,请让我吃掉:(