KM算法详解(例题为HDU2255 带权二分图的最优匹配):

此篇博客为转载并处理过的,希望能帮助你,如遇不足,请多见谅并指教:
希望你在阅读此篇博客时,你已经学会了匈牙利算法,这样会对你更有帮助哦!!!!!
对于KM算法自己的通俗理解与代码详解:
注:KM算法:就是在匈牙利基础上加了 权值 的束缚!
那么,为了达到权值和最大 ,或者 最小,就不能简单的去算最多的边数。
步骤:(以HDU2255 例题为例)
1.首先要找到所有居民愿意花钱最多的 那个房子。
题目中用到lx,ly数组,是为了同时调节两个数组,使得权值和最大。
或者说当要松弛的时候使得 本来最大的矛盾权值和 尽可能的损失小一些来得到 满足条件的最大权值和!
2.(lx[x]+ly[y]-w[x][y]=0)条件下进行匈牙利算法。
lx[x]+ly[y]-w[x][y]这个条件十分巧妙:(算法精髓)
1.即可以只让指定居民 找到 它愿意付最多钱的房子。
2.又可以在发生多居民抢一个房子时,用它来得到该居民到其它房子的松弛量!(即该居民到其它房子 比 到这个用钱最多的房子 愿意花的钱数上差的值。)
那么我们就要把 发生矛盾的居民到 其它房子的松弛量 的最小值求出来。再用它去松弛,就可以让原本矛盾的最大权值和,损失最小而得到满足条件的最大权值和

对于每个居民有4个基本问题:1.这个房子访问过没有?
2.这个房子能不能满足他的条件
3.这个房子是否被别人住了
4.被别人住了能不能得到调配

代码如下:

#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define MAX 310
#define INF 1<<25
#define clr(x) memset(x,0,sizeof(x))
int w[MAX][MAX];
int n;
int lx[MAX],ly[MAX];              /***lx[i]初始化为A集合中 i 点能到B集合某一点的最大权值, ly[i] 初始化0;***/ 
int link[MAX];
int slack[MAX];
int visx[MAX],visy[MAX];
bool find(int x)
{
    visx[x]=1;                    /****得到发生矛盾的居民集合****/
    for(int y=1;y<=n;y++)         /**这个居民,每个房子都去试一试!(找到就退出)**/
    {
        if(visy[y])               /****一个房子不需要重复访问****/
           continue;
        int t=lx[x]+ly[y]-w[x][y];/****按这个标准去用-匈牙利算法***/
        if(t==0)                  /**t==0标志这个房子可以给这位居民**/
        {
            visy[y]=1;
            if(link[y]==0||find(link[y])) /****这房子没人住 或 可以让住这个房子的人去找另外的房子住****/
            {
                link[y]=x;
                return true;              /**那么就可以让这位居民住进来**/
            }
        }
        else if(slack[y]>t)                 /**否则这个房子不能给这位居民!**/
            slack[y]=t;                    /***就要找到这个房子要松弛多少才能够给这位居民***/
                                           /***且当有多个居民都对这个房子有松弛量时,要找到最小的。****/
    }
    return false;                           /***一定不能忘记哦!***/ 
}
int KM()
{
    clr(lx);
    clr(ly);
    clr(link);
                                       /*****首先把每个居民出的钱最多的那个房子赋给它******/
    for(int i=1;i<=n;i++) 
        for(int j=1;j<=n;j++)
            if(lx[i]<w[i][j])
                lx[i]=w[i][j];
                                       /*****n循环-在满足了以前居民的情况下-给第i个居民安置房子*****/
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++) 
        slack[j]=INF;                   /***松弛量***//****这个松弛量不需要每次dfs都初始化一次;因为它跟visy有关***/
        while(1)                        /****死循环-在于一定要给这个居民找到房子为止!****/
        {
            clr(visx);
            clr(visy);
            if(find(i))                 /***找到房子就跳出死循环***/
            break;
            int d=INF;
            for(int k=1;k<=n;k++)
                if(!visy[k]&&d>slack[k])
                    d=slack[k];        /****找到最小松弛量*****/
            for(int k=1;k<=n;k++)      /****松弛操作-使发生矛盾的居民有更多选择*****/
            {
                if(visx[k])            /*****将矛盾居民的要求降低,使他们有更多房子可选择*****/
                     lx[k]-=d;
                if(visy[k])            /****使发生矛盾的房子在下一个子图,保持矛盾(即保持原子图性质)*****/
                    ly[k]+=d;
            }
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++)
        ans+=w[link[i]][i];
    return ans;

}
int main()
{
    while(~scanf("%d",&n))
    {
        clr(w);                        //每个案列都要重新置0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                scanf("%d",&w[i][j]);  //输入每边权值
        printf("%d\n",KM());
    }
    return 0;
}

一起学习,一起进步…..

猜你喜欢

转载自blog.csdn.net/qq_41995906/article/details/81238743