匈牙利算法

 看了文章:http://blog.csdn.net/china8848/article/details/2287769 和 http://www.cppblog.com/acronix/archive/2010/08/21/124175.html


http://blog.csdn.net/nomad2/article/details/7422232


其它资料(详细过程):http://www.byvoid.com/blog/hungary/

总结的很好。


算法过程如下:


记录匹配点的数组mx[n]、my[n],其中my下标标识右端的点,my[n]的元素的值标识左端与其相连的点,其值不为-1时表示匹配了(mx同理)。


从左端一个未匹配的点出发,依次遍历右端的点(只是寻找与它相连的点进行操作),如果与其相连并且可以寻找到增广路径(未匹配边 [——匹配边——未匹配边]... 交错),更新匹配信息,并且匹配值加1。


例如下图:


                                                                         


的算法实现测试程序如下:



#include <iostream>
using namespace std;

const int N = 1000;

int nx, ny;
int g[N][N];
int mx[N], my[N];
bool chk[N];

bool searchPath(int u)
{
        int v;

        for(v = 0; v < ny; v++)
        {
                if(g[u][v] && !chk[v])
                {
                        chk[v] = true;
                        if(my[v] == -1 || searchPath(my[v]))
                        {
                                my[v] = u;
                                mx[u] = v;
                                return true;
                        }
                }
        }
        return false;
}

int maxMatch()
{
        int u, ret = 0;
        memset(mx, -1, sizeof(mx));
        memset(my, -1, sizeof(my));
        for(u = 0; u < nx; u++)
        {
                if(mx[u] == -1)
                {
                        memset(chk, false, sizeof(chk));
                        if(searchPath(u))
                        {
                                ret++;
                        }
                }
        }
        return ret;
}
/*
void input()
{
		int i;
        for(i = 0; i < N; i++)
        {
                for(int j = 0; j < N; j++)
                {
                        g[i][j] = 0;
                }
        }

        cin >> nx >> ny;
        for(i = 0; i < nx; i++)
        {
                int n, y;
                cin >> n;
                for(int j = 0; j < n; j++)
                {
                        cin >> y;       
                        g[i][y] = 1;
                }
        }
      
        cout << endl;
}

void display()
{
	int i;
        for(i = 0; i < nx; i++)
        {
                cout << mx[i] << " ";
        }
        cout << endl;

        for(i = 0; i < ny; i++)
        {
                cout << my[i] << " ";
        }
        cout << endl;
}
*/
int main()
{
        //input();
		nx=4;
		ny=5;
		g[0][2]=1;
		g[1][0]=1;
		g[1][1]=1;
		g[1][1]=1;
		g[1][3]=1;
		g[2][2]=1;
		g[3][2]=1;
		g[3][3]=1;
		g[3][4]=1;
		g[0][3]=1;
        int m = maxMatch();     
        cout << "max match is " << m << endl;
       // display();
		return 0;
}



寻找增广路径过程:

对于左端的一个点i,如果右端的点j与其相连,如果i、j都是未匹配的点,则直接将两者 (i、j组成的边)加入原匹配并且匹配数加1,如果i是未匹配点,j是已匹配点,则从my[j]点开始寻找(从而达到交错路径的效果),并且如果能找到增广路径,则更新匹配信息,使my[j]=i,mx[i]=j,mx[ my[j]  ]=找到的增广路径中从my[j]连接的点(除了j以外的) ;相当于是把j、my[j]边匹配从原匹配中的删除,而把本次增广路径中的奇数边加入到原匹配中。

( 注:若点未匹配,则点所连接的所有边未匹配,若点匹配了该点连接的所有边中只有一条边是匹配了的)

(注:增广路径起始点和终点均为未匹配的点,中间经过的其它点均为已经匹配的,所以找增广路径的条件为右端当前点j未匹配或者 从 与该点匹配的左端点(即my[j])出发能找到增广路径)

例如对应于图中,程序执行过程为:


0出发:右端2与其相连并且未匹配,0-2边为增广路径,加入到原匹配,mx[0]=2,my[2]=0;

1出发:右端1与其相连并且未匹配,1-0边为增广路径,加入到原匹配,mx[1]=0,my[0]=1;


2出发:右端2与其相连,但是2已经匹配左端0,则从左端0开始看是否能找到增广路径,找到0-3时(0-3未匹配并且3是未匹配点,因此截止了)截止。

同时my[2]=2,mx[2]=2。mx[0]=2,my[3]=0。这一步相当于是把边0-2从原匹配中删除,把2-2、0-3加入原匹配。即增广路径中边取反的过程。


(左端未匹配点i - 右端匹配点j(与j匹配的点为my[j])-  左端my[j]开始找)


左端my[j]开始找表示执行与 上行中 括号左端 左端未匹配点i  相同的过程,有点类似递归了。。。


直到找到右端与其相连并且未匹配的点(刚好满足了从左端未匹配点i开始,从右端未匹配点z结束,并且中间穿插的是N(N>=0)对  匹配点j-与j匹配的点my[j]  i-j边 (未匹配)  ——>...——>  未匹配点z)则表示成功找到了增广路径。


PS:由于在遍历左边的时候,后面的点都完全没有可能参与到匹配中去,所以mx[i]==-1应该是一直成立的,所以代码中的mx数组可以去掉,只需要保留my即可。代码变为:


//二分图最大匹配的匈牙利算法详细请访问:http://blog.csdn.net/china8848
#include <memory.h>
#include <stdio.h>
//分别定义左右最大元素
#define LEFT_MAX 101
#define RIGHT_MAX 301
 
bool useif[RIGHT_MAX];
//link[]记录与右边元素连接的元素,-1表示没有连接
int link[RIGHT_MAX];
//定义左右两边元素
int left_num,right_num;
//array定义左右两边元素是否有连接
bool array[LEFT_MAX][RIGHT_MAX];
 
bool can(int t)
{
    int i;
   
    for(i=0;i<right_num;i++)
    {
       if(!useif[i]&&array[t][i])
       {
           useif[i]=true;
           if(link[i]==-1||can(link[i]))
           {
              link[i]=t;
              return true;
           }
       }
 
    }
    return false;
}
 
int main()
{
    int j,i,k,num,count,temp,temp2;
    //array,num清零,link清为-1
    memset(link,0xFF,sizeof(link));
    memset(array,0,sizeof(array));
   
    num=0;
    //匹配,num为结果
    for(i=0;i<left_num;i++)
    {
       memset(useif,0,sizeof(useif));
       if(can(i))
           num++;
    }
    printf("%d/n",num);
    }
    return 1;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值