二分图最佳匹配有匹配后总权值最大和最小两种,求最佳匹配的一个有效算法是KM算法。KM算法很复杂很费解。现在只能总结步骤。
KM算法步骤( 以下有层次关系,㈠ > 1 > (1) > ① > Ⅰ)
(一) 建图:所有点分为两部分,一部分属于L端,一部分属于R端,两端的点各有n个,现在要为L端的所有点在R端的点中找它们的匹配,使最后总权值最大或最小,设有一条边联通L的i点与R的j点,权值为w[i][j],设二维数组gragh_m保存图,graph_m[i][j]= w[i][j]。
(二) 为每个点设一个顶标,L端的为pl,R端的为pr,将pr的全赋为0,pl全赋为与这个点联通的权值最大的边的权值,例如与L端第i个联通的最大权值的边的权值为m,则pl[i]=m。
(三) 用一个循环枚举L中的每个点,为每个点在R中找其最佳匹配
1.设一辅助数组slack(可在全局定义)大小为n,将slack全赋为MAX_INT(无穷大)。
2、用一个死循环为L中的每个点找匹配,直到找到这个匹配时候break掉此循环,
⑴定义两个数组vl,vr,全赋为0,分别表示L中的某个点没有被访问过和R中的某个点没有被需要过(需要表示R中的某个点满足L中的某个点的最佳匹配要求,但被需要不表示最后能肯定配到它)
⑵以当前的为其找匹配的点作为实参调用匹配函数match,这里假定这个点位置为pos
match 函数体如下:
① 将vl[pos]赋为1(访问过…)
② 用个循环枚举R中的每个点,设当前点为j,如果这个点还没有被需要过(vr[j]==0)则执行:定义变量val=pl[pos]+pr[j]-graph_m[pos][j];
如果val==0(满足匹配条件)
{
Ⅰ.vr[j]=1;
Ⅱ.如果pre[j]==-1或者match(pre[j])==1(pre[j]表示L中与R中j点的匹配点),则pre[j]=pos,返回真。
}
否则如果(val>0)
{
如果val<slack[j],则slack[j]=val;
}
③返回假。
⑶.如果match返回真,break当前循环。
⑷定义变量d=MAX_INT,枚举R中每个点,如果vr[j]==0(没被访问过)并且slack[j]<val,则d赋值为slack[kj]。
⑸枚举L中每个点和R中每个点,假设当前点为j,如果vl[j]=1,则pl-=d;如果vr[j]==1,则pr[j]+=d;
3.将匹配后每个匹配对的权值加起来。
KM算法C++模板,模板的注释为上面所描述的步骤
}
if(val>0&&val<slack[j])//(三)->2->(2)->②
slack[j]=val;
}
}
return false;//(三)->2->(2)->③
}
int km()
{
int i,j,res=0,d;
memset(pr,0,sizeof(pr)); ///// (二)
fill(pl,pl+105,INT_MAX); ///// (二)
memset(pre,-1,sizeof(pre));
for(i=0;i<scale;i++) ////// (三)
{
fill(slack,slack+105,INT_MAX); // (三)->1
while(true)
{
memset(vl,0,sizeof(vl)); // (三)->2->(1)
memset(vr,0,sizeof(vr)); // (三)->2->(1)
if(match(i)) // (三)->2->(2) 请进入match函数体
break; // (三)->2->(3)
d=INT_MAX; //(三)->2->(4)
for(j=0;j<scale;j++) // (三)->2->(4)
if(!vr[j]&&slack[j]<d)
d=slack[j];
for(j=0;j<scale;j++)//(三)->2->(5)
{
if(vl[j])
pl[j]-=d;
if(vr[j])
pr[j]+=d;
}
}
}
for(j=0;j<scale;j++) //(三)->3
res+=graph_w[pre[j]][j];
return res;
}
最佳匹配题目
POJ2195 这里求的是最小匹配,模板的返回值要变成负的,代码
}
if(val>0&&val<slack[j])//(三)->2->(2)->②
slack[j]=val;
}
}
return false;//(三)->2->(2)->③
}
int km()
{
int i,j,res=0,d;
memset(pr,0,sizeof(pr)); ///// (二)
fill(pl,pl+105,INT_MAX); ///// (二)
memset(pre,-1,sizeof(pre));
for(i=0;i<scale;i++) ////// (三)
{
fill(slack,slack+105,INT_MAX); // (三)->1
while(true)
{
memset(vl,0,sizeof(vl)); // (三)->2->(1)
memset(vr,0,sizeof(vr)); // (三)->2->(1)
if(match(i)) // (三)->2->(2) 请进入match函数体
break; // (三)->2->(3)
d=INT_MAX; //(三)->2->(4)
for(j=0;j<scale;j++) // (三)->2->(4)
if(!vr[j]&&slack[j]<d)
d=slack[j];
for(j=0;j<scale;j++)//(三)->2->(5)
{
if(vl[j])
pl[j]-=d;
if(vr[j])
pr[j]+=d;
}
}
}
for(j=0;j<scale;j++) //(三)->3
res+=graph_w[pre[j]][j];
return -res;
}
int main()
{
int N,M,i,j,mnum,hnum;
char graph[105][105];
while(cin>>N>>M&&N!=0&&M!=0)
{
mnum=0;
hnum=0;
for(i=0;i<N;i++)
for(j=0;j<M;j++)
{
cin>>graph[i][j];
if(graph[i][j]=='m')
{
pm[mnum].r=i;
pm[mnum++].c=j;
}
if(graph[i][j]=='H')
{
ph[hnum].r=i;
ph[hnum++].c=j;
}
}
scale=mnum;
for(i=0;i<scale;i++)
for(j=0;j<scale;j++)
graph_w[i][j]=-( abs( pm[i].r-ph[j].r )+abs( pm[i].c-ph[j].c ) );
cout<<km()<<endl;
}
return 0;
}