hdu 2444 (二分图判断与自最大二分图匹配)

题意:

有n个学生,有m对人是认识的,每一对认识的人能分到一间房,问能否把n个学生分成两部分,每部分内的学生互不认识,而两部分之间的学生认识。如果可以分成两部分,就算出房间最多需要多少间,否则就输出No。

分析:

首先,能否分为两部分,使,每部分内的学生互不相识。即判断是否是二分图,使用染色法。然后,学生中每一对互相认识的学生可以分配到一个房间,求最大的房间数,就是求二分图匹配的最大数量。

判断是否为二分图:

无向图G为二分图的充分必要条件是,G至少用两个顶点,且其所有的回路的长度均为偶数。即判断,是否存在奇圈。方法:染色判断法:任选某个起点为染色起点,同时把相邻有边的点染色为对立的点,通过bfs或者dfs完成染色,若走到某一步发现当前点和相邻的点颜色相同,说明出现了奇圈,矛盾。

匈牙利算法求最大匹配数:

原理 通过dfs或者bfs进行遍历,遇到新的未匹配的点或者有增广路径(若有增广路径就可以反转,则最大匹配数就可以加1)返回值为1则最大匹配数+1,然后继续遍历,直到完成遍历或者已经找到最大匹配数。

匈牙利算法的正确性在于贪心策略(hungry),重要特点:当一个节点成为匹配点的时候,至多因为找到增广路而更换匹配对象,而不会再变回非匹配点

一题一式:一般的二分模板题的话套路是给你两列数,不同数组之间有关系,每个组之间没有联系,求最大匹配。本题中,给定一个母图,要求再母图中进行分割,分成两部分,所有的点满足形式上的二分图。(注意是两列,每一列数都包含了所有的数,然后进行匹配,问题核心代码:linker[x]=i,linker[i]=x)

代码:

#include
#include
#include
#include
#include
#include
using namespace std;

const int maxn=222;
const int Maxn=1000006;
int e[maxn][maxn];
int tag[maxn];
int linker[maxn];
int used[maxn];
int n,m;

int bfs(int st){
	memset(tag,-1,sizeof(tag));
	queueq;
	q.push (st);
	tag[st]=1;
	while(!q.empty()){
		int temp=q.front ();
		q.pop();
		for(int i=1;i<=n;i++){
			if(e[temp][i]){
				if(tag[i]==-1){
					tag[i]=(tag[temp]+1)%2;
					q.push (i);
				}else if(tag[i]==tag[temp]){
					return 1;
				}
			}
		}
	}
	return 0;
}

int dfs(int u){
	for(int i=1;i<=n;i++){
		if(e[u][i]&&!used[i]){
			used[i]=1;
			if(linker[i]==-1||dfs(linker[i])){
				linker[u]=i;linker[i]=u;//***自最大二分图匹配核心
				return 1;
			}
		}
	}
	return 0;
}

int hungry(void){
	 int res=0;
	 memset(linker,-1,sizeof(linker));
	 for(int u=1;u<=n;u++){
		if(linker[u]!=-1)continue;
		memset(used,0,sizeof(used));
		if(dfs(u)){
			res++;
		}
	 }
	 return res;
}

int main(){
	while(~scanf("%d%d",&n,&m)){
		memset(tag,-1,sizeof(tag));
		memset(e,0,sizeof(e));
		while(m--){
			int x,y;
			scanf("%d%d",&x,&y);
			e[x][y]=e[y][x]=1;
		}
		if(bfs(1)){
			printf("No\n");
		}else{
			printf("%d\n",hungry());
		}
	}
}

你可能感兴趣的:(二分图匹配)