题干:
大黑山上有小小民和小小涛两种物种,山东人小李想要研究这两种物种的关系
奇怪的是大黑山上有相同数量的小小民和小小涛。小李数了数一共有 P 个,小李分别给P个小小民和小小涛编号 1 - P 号,已知每对小小民之间都是好朋友,每对小小涛之间也都是好朋友,但是 i 号小小民和 j 号小小涛不一定是好朋友
山东人小李想要知道在这个物种间最大的友好群体的物种数量(在这个群体中任意两个生物都是好朋友),你能帮小李解决这个难题嘛?
Input
第一行一个整数T,代表测试数据数量
每个测试数据第一行有两个整数 P N ,大黑山上小小民或者小小涛的数量 P ,和他们之间有 N 个关系 ( 1<= P <=20 ),( 0<= N <= P^2 )
接下来 N 行每行两个整数 mi ti 表示 mi 编号的小小民和 ti 编号的小小涛是好朋友
Output
对于每个测试数据,输出一个整数表示最大的友好群体中生物的数量
Sample Input
2
4 5
1 2
2 2
3 2
4 2
1 4
3 4
1 2
2 1
1 1
2 2
Sample Output
5
4
解题报告:
直接跑补图最大独立集就行了=二分图中顶点数-最小点覆盖。
AC代码:
using namespace std;
typedef pair<int,int> PII;
const int MAX = 2e5 + 5;
int p,n;
bool line[22][22];
int nxt[22];
int use[22];
bool find(int x) {
for(int i = 1; i<=p; i++) {
if(line[x][i] && use[i] == 0) {
use[i] = 1;
if(nxt[i] == 0 || find(nxt[i])) {
nxt[i] = x;
return 1;
}
}
}
return 0 ;
}
int match() {
int res = 0;
for(int i = 1; i<=p; i++) {
memset(use,0,sizeof use);
if(find(i)) res++;
}
return res;
}
int main()
{
int t;
cin>>t;
while(t--) {
memset(line,1,sizeof line);
memset(nxt,0,sizeof nxt);
scanf("%d%d",&p,&n);
for(int a,b,i = 1; i<=n; i++) {
scanf("%d%d",&a,&b);
line[a][b]=0;
}
printf("%d\n",2*p-match());
}
return 0 ;
}
法二:(1400ms竟然能过)
状压dp,复杂度O(n*2^n)枚举
using namespace std;
typedef pair<int,int> PII;
const int MAX = 2e5 + 5;
int bit[22];
int lowbit(int x) {return x&-x;}
int main()
{
int t,p,n;
cin>>t;
while(t--) {
scanf("%d%d",&p,&n);
memset(bit,0,sizeof bit);
for(int a,b,i = 1; i<=n; i++) {
scanf("%d%d",&a,&b);
a--;b--;
bit[a] |= 1<<b;
}
int up = 1<<p,ans = 0;
for(int sta = 0; sta<up; sta++) {//枚举选择了哪些男生
int tmp = up-1;//记录当前男生情况下可以获得的所有女生
for(int i = 0; i<p; i++) {
if(sta&(1<<i)) tmp &= bit[i];
}
int sum = 0;
for(int i = tmp; i!=0; i-=lowbit(i)) {
sum++;
}
for(int i = sta; i!=0; i-=lowbit(i)) sum++;
ans = max(ans,sum);
}
printf("%d\n",ans);
}
return 0 ;
}