(算法)二分图的最大匹配(匈牙利算法)

  先说下二分图的概念吧:二分图是指图可以分成两个点集X和Y,那么任意一条边两个端点分属于不同的点集,也就是说任意一条边的的一个端点在X中,另外一个端点在Y中。并且同一个点集中的任意点之间是没有边相连的,满足这样的性质的图就是二分图。

       匹配是指:在给定的一个二分图G中的一个边的子集构成的集合M,M中的任意两条边都没有公共的顶点,那么M就称为一个匹配。那么匹配中含有边数最多的匹配就是最大匹配。

       下面来说说匈牙利算法是如何求解二分图的最大匹配的吧。

       匈牙利算法是通过不断的找增广路径来扩大匹配集中匹配的边数,我们对左边点集的每一个点找增广路径,直到找不到了增广路径,那么这个时候的匹配就是最大匹配。

       匈牙利算法的基本思想就是这样,如果想要具体了解,自己画个图理解下就行了,很容易理解的。下面我们来说说匈牙利算法是如何实现的吧。

       首先我们需要构建一个二分图,这个随你。之后为了找增广路径,我们需要一个match[i]表示右边点集的i的匹配的左边的点,如果是-1,那么表示还没有找到匹配的点;这个数组在我们找增广路径的时候非常重要。

        其次我们需要一个vis数组,用来表示在每一次找增广路径的时候左边的点是否被访问过,这个是为了防止重复找同一个点而出现错误。

        我们再找增广路径的时候,如果一个点的match[i]==-1,那么我们就找到了一条增广路径,返回1;否则我们继续找,对match[i]进行dfs,如果最终都没有找到增广路径,那么就只好返回零表示没有找到增广路径。

       基于这个思想,我把在POJ上的2239那道题的代码贴上来,匈牙利算法基本都是差不多的,有需要的可以拿过去参考一下。呵呵——

#include 
#include 
#include 
#define LMAX 310
#define RMAX 90
int edge[LMAX][RMAX];
int vis[RMAX],match[RMAX];
int RN;//左边的顶点的个数,右边顶点的个数


int dfs(int i)
{
    int j;
    for(j=1;j<=RN;j++)
    {
        if(edge[i][j]&&vis[j]==0)
        {
            vis[j]=1;
            if(match[j]==-1)
            {
                match[j]=i;
                return 1;
            }
            else
            {
                if(dfs(match[j]))
                {
                    match[j]=i;
                    return 1;
                }
            }
        }
    }
    return 0;
}

int solve(int LN)
{
    memset(match,-1,sizeof(match));
    int i,sum=0;
    for(i=1;i<=LN;i++)
    {
            memset(vis,0,sizeof(vis));
            if(dfs(i))
                sum++;
    }
    return sum;
}

int main()
{
    int n,t;
    int i;
    int a,b;
    RN=7*12;
//    freopen("data.txt","r",stdin);
    while(scanf("%d",&n)!=EOF)
    {
        memset(edge,0,sizeof(edge));
        for(i=1;i<=n;i++)
        {
            scanf("%d",&t);
            while(t--)
            {
                scanf("%d %d",&a,&b);
                edge[i][(a-1)*12+b]=1;
            }
        }

        printf("%d\n",solve(n));

    }
    return 0;
}



你可能感兴趣的:(Algorithm)