poj 3692 & 2771 二部图最大独立集(选认识的孩子玩游戏)

本文介绍了如何通过图论的方法解决最大团问题,并详细解释了如何将最大团转化为二部图中的最大独立集,进而求解。通过实例代码展示了具体的实现过程,包括使用匈牙利算法求解最大匹配问题,最终得出最大团的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

3692题意:所有男孩都互相认识,所有女孩也互相认识。又已知有若干男生和女生互相认识。现在要选出最大的孩子集合,使得他们之间全部互相认识。

2771题意:几乎同上。只是多了一步需要判断男女能否在一起玩。之后的做法同3692.

思路:思路比较明确,如果把所有孩子看成顶点,认识连边构成图,则题意为求图的最大团。由最大团=补图的最大独立集可知相当于求补图的最大独立集。显然补图是二部图。那么根据二部图中最大独立集+最大匹配=V可知相当于求V-最大匹配。

注意一点:求二部图最大独立集的时候,下述算法是错误的:对于每个联通块进行黑白染色,然后把max(黑点数,白点数)加进答案。因为这样相当于是选择二部图中点数多的那部加入最大独立集中,但是实际上最大独立集可能在两部中都有点,而不一定选择一部的所有点,而另一部一个不选。反例如乙炔的碳骨架(把中间的双键用一条边相连)所形成的图,其二部图染色只有一种染色法,每一部都是三个点。但是其最大独立集是4个叶子组成的点集。

3692代码:

#include <stdio.h>
#include <string.h>
#define N 205
int n,m,k;
int used[N],link[N],g[N][N],c=1;
int dfs(int i){
	int j;
	for(j = 1;j<=m;j++)
		if(!used[j] && g[i][j]){
			used[j] = 1;
			if(link[j] == -1 || dfs(link[j])){
				link[j] = i;
				return 1;
			}
		}
	return 0;
}
int hungary(){
	int i,res=0;
	for(i = 1;i<=n;i++){
		memset(used,0,sizeof(used));
		if(dfs(i))
			res++;
	}
	return res;
}
int main(){
	while(scanf("%d %d %d",&n,&m,&k) && (m+n+k)){
		int i,j,a,b;
		memset(g,0,sizeof(g));
		memset(link,-1,sizeof(link));
		for(i = 0;i<k;i++){
			scanf("%d %d",&a,&b);
			g[a][b] = 1;
		}
		for(i = 1;i<=n;i++)
			for(j = 1;j<=m;j++)
				g[i][j] ^= 1;
		printf("Case %d: %d\n",c++,n+m-hungary());
	}
	return 0;
}
2771代码:
#include <stdio.h>
#include <string.h>
#include <math.h>
#define N 505
int T,n,m,sum;
struct node{
	int h;
	char s[100],t[100];
}boy[N],girl[N];
int g[N][N],used[N],link[N];
int dfs(int i){
	int j;
	for(j = 1;j<=m;j++)
		if(!used[j] && g[i][j]){
			used[j] = 1;
			if(link[j] == -1 || dfs(link[j])){
				link[j] = i;
				return 1;
			}
		}
	return 0;
}
int hungary(){
	int i,res=0;
	for(i = 1;i<=n;i++){
		memset(used,0,sizeof(used));
		if(dfs(i))
			res++;
	}
	return res;
}
int test(int a,int b){
	if(abs(boy[a].h-girl[b].h)>40)
		return 1;
	if(strcmp(boy[a].s,girl[b].s))
		return 1;
	if(!strcmp(boy[a].t,girl[b].t))
		return 1;
	return 0;
}
int main(){
	scanf("%d",&T);
	while(T--){
		int i,j;
		char ch;
		n = m = 0;
		memset(link,-1,sizeof(link));
		memset(g,0,sizeof(g));
		scanf("%d",&sum);
		for(i = 0;i<sum;i++){
			scanf("%d %c",&j,&ch);
			if(ch == 'M'){
				boy[++n].h = j;
				scanf("%s %s",boy[n].s,boy[n].t);
			}else{
				girl[++m].h = j;
				scanf("%s %s",girl[m].s,girl[m].t);
			}
		}
		for(i = 1;i<=n;i++)
			for(j = 1;j<=m;j++)
				if(test(i,j))
					g[i][j] = 1;
		for(i = 1;i<=n;i++)
			for(j = 1;j<=m;j++)
				g[i][j] ^= 1;
		printf("%d\n",n+m-hungary());
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值