题目
两个大小为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;
}