Gym 101915D Largest Group (二进制枚举/状压dp/二分图最大团)

题目

两个大小为P(|P|<=20)的集合,每个集合是一个团

给定一些二分图的边的关系N(N<=P*P),

问最多选定多少个点,使得所选集合是一个团

思路来源

耀宗、励宁、马老师等

https://www.cnblogs.com/qywhy/p/9745048.html

题解

首先,把边的关系压入数组集合,按位维护

二进制枚举:每次去判断二进制枚举的左边集合,所对应的右边集合的交集,若干个集合的交集是当前集合的答案

状压dp:每次判断二进制枚举的左边集合,与去掉lowbit(i)的集合相比,需要位运算与一个lowbit(i)的集合

注意__builtin等函数的用法

代码1(状压dp)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long ll;
int t,p,n;
int s[25],dp[(1<<20)+10];
int a,b,out,all;
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&p,&n);
		out=0;all=1<<p;
		for(int i=0;i<all;++i)dp[i]=0;
		for(int i=1;i<=p;++i)s[i]=0;
		for(int i=1;i<=n;++i)
		{
			scanf("%d%d",&a,&b);
			s[a]|=(1<<(b-1));
		}
		dp[0]=all-1;out=p;
		for(int i=1;i<all;++i)
		{
			int pos=__builtin_ffs(i);//第一个1是从右数第几位 
			dp[i]=dp[i^(1<<(pos-1))]&s[pos];
		    out=max(__builtin_popcount(dp[i])+__builtin_popcount(i),out);
		}
		printf("%d\n",out);
	}
	return 0;
} 

代码2(二分图最大团=补图最大独立集=两边所有的顶点-补图最大匹配数)

至于怎么建补图,邻接矩阵直接标出来这些边为-1,遇到-1就跳过就好了

现在还不是很确定什么时候hungary返回res什么时候返回res/2,但还是看重没重复叭

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int T,num;
int n,m;
int link[50];
bool vis[50];
int mp[50][50];
bool dfs(int u)
{
	for(int i=n+1;i<=2*n;++i)
	{
		if(~mp[u][i])
		{ 
		 if(!vis[i])
		 {
			vis[i]=1;
			if(link[i]==-1||dfs(link[i]))
			{
				link[i]=u;
				return 1;
			}
		 }
	    }
	}
	return 0;
}
int hungary()
{
	int res=0;
	memset(vis,0,sizeof vis);
	memset(link,-1,sizeof link);
	for(int u=1;u<=n;++u)
	{
		memset(vis,0,sizeof vis);
		res+=dfs(u);
	}
	return res;
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		memset(mp,0,sizeof mp);
		scanf("%d%d",&n,&m);
		//1-n 左集合 (n+1)-2*n 右集合 
		for(int i=1;i<=m;++i)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			mp[u][n+v]=mp[n+v][u]=-1;
		}
		num=hungary();
		printf("%d\n",2*n-num);
	} 
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Code92007/article/details/89200193
今日推荐