二分匹配之一

二分图的最大匹配:

匈牙利算法

DFS实现的匈牙利算法是每一次寻找一条增广路径

时间复杂度是O(n^3),复杂度偏高,通过改进优化在寻找增广路经的时候同时找多条不相交的增广路经

在寻找路径的每一个阶段,找到的增广路经都具有相同的长度,时间复杂度是O(n^0.5*m).

模板:DFS实现:

//DFS实现增广,复杂度O(n^3);
const int maxn=550;
bool bmap[maxn][maxn];
bool bmask[maxn];
int cx[maxn],cy[maxn];
int nx,ny;

int findpath(int u)
{
	int i,j;
	for(int i=0;i<ny;i++)
	{
		if(bmap[u][i]&&!bmask[i])
		{
			bmask[i]=1;
			if(cy[i]==-1||findpath(cy[i]))
			{
				cy[i]=u;
				cx[u]=i;
				return 1;//每次增广只能找到一条增广路
			}
		}
	}

	return 0;
}

int maxMatch()
{
	int res(0);
	for(int i=0;i<maxn;i++)
	{
		cx[i]=-1;
		cy[i]=-1;
	}

	for(int i=0;i<nx;i++)
	{
		if(cx[i]==-1)
		{

			memset(bmask,0,sizeof(bmask));
			res+=findpath(i);

		}
	}
	return res;
}

模板2:

//时间复杂度是O(n^0.5*m).
const int maxn=510;//最大顶点数
const int inf=1<<28;
int bmap[maxn][maxn];
int cx[maxn];
int cy[maxn];
int nx,ny;
int dx[maxn];//dx[i]表示左集合i顶点的距离标号
int dy[maxn];//dy[i]表示左集合i顶点的距离标号
int dis;
bool bmask[maxn];
bool searchpath()
{
	queue<int> Q;
	dis=inf;
	for(int i=0;i<maxn;i++)//初始化为-1
	{
		dx[i]=-1;
		dy[i]=-1;
	}

	for(int i=0;i<nx;i++)
	{
		if(cx[i]==-1)
		{
			Q.push(i);dx[i]=0;
		}
	}
	while(!Q.empty())
	{
		int u=Q.front();Q.pop();
		if(dx[u]>dis)break;
		for(int v=0;v<ny;v++)
		{
			if(bmap[u][v]&&dy[v]==-1)
			{
				dy[v]=dx[u]+1;
				if(cy[v]==-1)dis=dy[v];
				else
				{
					dx[cy[v]]=dy[v]+1;
					Q.push(cy[v]);
				}
			}

		}
	}
	return dis!=inf;
}

int findpath(int u)
{

	for(int i=0;i<ny;i++)
	{
		if(bmap[u][i]&&!bmask[i]&&dy[i]==dx[u]+1)
		{
			bmask[i]=1;
			if(cy[i]!=-1&&dy[i]==dis)
			{
				continue;
			}
			if(cy[i]==-1||findpath(cy[i]))
			{
				cy[i]=u;
				cx[u]=i;
				return 1;
			}
		}
	}
	return 0;
}

int maxMatch()
{
	int res(0);
	for(int i=0;i<maxn;i++)
	{
		cx[i]=cy[i]=-1;
	}
	while(searchpath())
	{
		memset(bmask,0,sizeof(bmask));
		for(int i=0;i<nx;i++)
		{
			if(cx[i]==-1)
			{
				res+=findpath(i);
			}

		}

	}
	return res;
}


二分图的多重匹配:

即一对多的形式:

模板:

const int maxn=1001;
int bmap[maxn][maxn];
bool bmask[maxn];
int nx,ny;
int vcy[maxn];//vcy[i]表示右集合i顶点匹配到左集合的顶点数目。
int cy[maxn][maxn];//cy[i][j]表示与右边第i个匹配的第j个元素
int _left,_right,limit;//注意left,right是限制字,不能声明变量。

bool findpath(int u)
{
	for(int i=0;i<ny;i++)
	{
		if(bmap[u][i]&&!bmask[i])
		{
			bmask[i]=1;
			if(vcy[i]<limit)
			{
				cy[i][vcy[i]++]=u;
				return true;
			}
			for(int j=0;j<vcy[i];j++)
			{
				if(findpath(cy[i][j]))
				{
					cy[i][j]=u;
					return true;
				}
			}

		}

	}
return false;
}

bool MulMatch()
{
	memset(vcy,0,sizeof(vcy));
	for(int i=0;i<nx;i++)
	{
		memset(bmask,0,sizeof(bmask));
		if(!findpath(i))return false;
	}
	return true;
}


几个例题:

poj2289 典型的二分图多重匹配+二分求limit

//============================================================================
// Name        : 二分图的多重匹配.cpp
// Author      : xinge008
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
//此算法解决的是X边可以链接Y边的多个点,而Y边只能链接X边的一个点。即一对多
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include<stdlib.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <list>
using namespace std;
const int maxn=1001;

char ch[2010];
int bmap[maxn][maxn];
bool bmask[maxn];
int nx,ny;
int vcy[maxn];//vcy[i]表示右集合i顶点匹配到左集合的顶点数目。
int cy[maxn][maxn];//cy[i][j]表示与右边第i个匹配的第j个元素
int _left,_right,limit;//注意left,right是限制字,不能声明变量。

bool findpath(int u)
{
	for(int i=0;i<ny;i++)
	{
		if(bmap[u][i]&&!bmask[i])
		{
			bmask[i]=1;
			if(vcy[i]<limit)
			{
				cy[i][vcy[i]++]=u;
				return true;
			}
			for(int j=0;j<vcy[i];j++)
			{
				if(findpath(cy[i][j]))
				{
					cy[i][j]=u;
					return true;
				}
			}

		}

	}
return false;
}

bool MulMatch()
{
	memset(vcy,0,sizeof(vcy));
	for(int i=0;i<nx;i++)
	{
		memset(bmask,0,sizeof(bmask));
		if(!findpath(i))return false;
	}
	return true;
}
int main() {
	while(scanf("%d%d",&nx,&ny)!=EOF)
	{
		getchar();
		if(nx==0||ny==0)
			break;
		memset(bmap,0,sizeof(bmap));
		for(int i=0;i<nx;i++)
		{
			gets(ch);
			int len=strlen(ch);
//			printf("%d\n",len);
			for(int j=0;j<len;j++)
			{
				if(ch[j]>='0'&&ch[j]<='9')
				{
					int v=0;
					while(ch[j]>='0'&&ch[j]<='9')
					{
				       v=v*10+ch[j++]-'0';
					}
//					printf("%d\n",v);
					bmap[i][v]=1;
				}
			}
		}
		_left=0,_right=nx;
		while(_left<_right)
		{
			limit=(_left+_right)>>1;
			if(MulMatch())_right=limit;
			else
				_left=limit+1;
		}
		printf("%d\n",_left);
	}

	return 0;
}


hdu3605,典型多重匹配:


//============================================================================
// Name        : hdu3065二分图的多重匹配.cpp
// Author      : xinge008
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include<stdlib.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <list>
using namespace std;
const int maxn=15;
int bmap[100010][maxn];
bool bmask[maxn];
int lit[maxn];
int nx,ny;
int vcy[maxn];//vcy[i]表示右集合i顶点匹配到左集合的顶点数目。
int cy[maxn][100010];//cy[i][j]表示与右边第i个匹配的第j个元素
int limit;//注意left,right是限制字,不能声明变量。

bool findpath(int u)
{
	for(int i=0;i<ny;i++)
	{
		if(bmap[u][i]&&!bmask[i])
		{
			bmask[i]=1;
//			printf("%d %d\n",vcy[i],lit[i]);
			if(vcy[i]>lit[i])
				return false;
			if(vcy[i]<lit[i])
			{
				cy[i][vcy[i]++]=u;
				return true;
			}
			for(int j=0;j<vcy[i];j++)
			{
				if(findpath(cy[i][j]))
				{
					cy[i][j]=u;
					return true;
				}
			}

		}

	}
return false;
}

bool MulMatch()
{
	memset(vcy,0,sizeof(vcy));
	for(int i=0;i<nx;i++)
	{
		memset(bmask,0,sizeof(bmask));
		if(!findpath(i))return false;
	}
	return true;
}

int main() {

	while(~scanf("%d%d",&nx,&ny))
	{
		memset(bmap,0,sizeof(bmap));
		for(int i=0;i<nx;i++)
		{
			for(int j=0;j<ny;j++)
			{
				int num;
				scanf("%d",&num);
				bmap[i][j]=num;
			}

		}
		for(int i=0;i<ny;i++)
			scanf("%d",&lit[i]);

		if(MulMatch())
			printf("YES\n");
		else
			printf("NO\n");

	}
	return 0;
}

hdu2063 裸的二分图的最大匹配:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
using namespace std;
//DFS实现增广,复杂度O(n^3);
const int maxn=550;
bool bmap[maxn][maxn];
bool bmask[maxn];
int cx[maxn],cy[maxn];
int nx,ny;

int findpath(int u)
{
    int i;
    for(i=0;i<ny;i++)
    {
        if(bmap[u][i]&&!bmask[i])
        {
            bmask[i]=1;
            if(cy[i]==-1||findpath(cy[i]))
            {
                cy[i]=u;
                cx[u]=i;
                return 1;//每次增广只能找到一条增广路
            }
        }
    }

    return 0;
}

int maxMatch()
{
    int res(0);
    for(int i=0;i<maxn;i++)
    {
        cx[i]=-1;
        cy[i]=-1;
    }

    for(int i=0;i<nx;i++)
    {
        if(cx[i]==-1)
        {

            memset(bmask,0,sizeof(bmask));
            res+=findpath(i);

        }
    }
    return res;
}
int main() {
    int T;
    while(~scanf("%d",&T))
    {
        if(T==0)
            break;
        scanf("%d%d",&nx,&ny);
        for(int i=0;i<maxn;i++)
            for(int j=0;j<maxn;j++)
                bmap[i][j]=false;
        int boy,girl;
        for(int i=1;i<=T;i++)
        {
            scanf("%d%d",&boy,&girl);
            boy--;girl--;
            bmap[boy][girl]=true;
        }
        printf("%d\n",maxMatch());
    }


    return 0;
}

二分图的重要性质:

二分图的最小顶点覆盖=二分图的最大匹配数

二分图的最大独立集=顶点数-二分图的最大匹配数 //当然了,有时候最大独立集=顶点数-二分图的最大匹配数/2.
//理解:最大独立集其实就是先求出二分图的最大匹配,然后用总的顶点数减去(最大匹配*2)+最大匹配,之所以加上最大匹配就是因为最大独立集要求是孤立点+各最大匹配中的一个点:即为最大匹配的个数。

二分图的最小路径覆盖=顶点数-二分图的最大匹配数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值