题目描述:
在很久很久以前,曾经有两个国家和睦相处,无忧无虑的生活着。一年一度的评比大会开始了,作为和平的两国,一个朋友圈数量最多的永远都是最值得他人的尊敬,所以现在就是需要你求朋友圈的最大数目。
两个国家看成是AB两国,现在是两个国家的描述:
- A国:每个人都有一个友善值,当两个A国人的友善值a、b,如果a xor b mod 2=1,
那么这两个人都是朋友,否则不是; - B国:每个人都有一个友善值,当两个B国人的友善值a、b,如果a xor b mod 2=0
或者 (a or b)化成二进制有奇数个1,那么两个人是朋友,否则不是朋友; - A、B两国之间的人也有可能是朋友,数据中将会给出A、B之间“朋友”的情况。
- 在AB两国,朋友圈的定义:一个朋友圈集合S,满足S∈A∪ B,对于所有的i,j∈ S ,i 和 j 是朋友
由于落后的古代,没有电脑这个也就成了每年最大的难题,而你能帮他们求出最大朋 友圈的人数吗?
题目分析:
求图的最大团。
最大团等于补图的最大独立集。
由于这个问题是nphard问题,所以我们要观察这个图的特殊性。
对于A集合:
奇数和偶数之间有边,所以A集合中最多选两个。
对于B集合:
奇数和奇数之间都有边,偶数和偶数之间都有边,奇数和偶数之间可能右边。
那么对于这B集合的补图,就一定是,奇数之间相互无边,偶数之间相互无边,奇数和偶数构成二分图。
二分图最大独立集等于二分图总点数减去最大匹配数。
所以对于A集合我们枚举选择哪些点(不选,选一个,选一奇一偶)
然后对于B集合中不为A集合中选中点朋友的点,删去。
对剩下的B集合的点跑二分图最大匹配,找出最大团即可。
能用时间戳的地方就不要用memset,省时
(输入中说多组数据然而并没有,好迷啊=。=)
代码如下:
#include <cstdio>
#include <iostream>
#define N 3100
using namespace std;
inline int Max(int x,int y) { return x>y?x:y; }
int A,B,AB,ans=0;
int a[N],b[N];
int odd[N],even[N],cnto,cnte;
int match[N],tim[N];
int fir[N],nes[N*N],v[N*N],tot=1;
int ban[N],vis[N];
bool e[N][N];
int T1,T2;
void edge(int x,int y)
{
v[++tot]=y;
nes[tot]=fir[x];
fir[x]=tot;
}
bool Count(int x,int y)
{
int tmp=x^y,cnt=0;
if((tmp&1)==0) return false;
tmp=x|y;
for(;tmp;tmp-=tmp&-tmp) cnt++;
return !(cnt&1);
}
bool Hungary(int c)
{
for(int t=fir[c];t;t=nes[t])
{
if(ban[v[t]]==T1 || vis[v[t]]==T2) continue;
vis[v[t]]=T2;
if(tim[v[t]]!=T1 || Hungary(match[v[t]]))
{
match[v[t]]=c;
match[c]=v[t];
tim[v[t]]=tim[c]=T1;
return true;
}
}
return false;
}
int calc(int x=0,int y=0)
{
int ans=B;
T1++;
for(int i=1;i<=B;i++)
if((x && !e[x][i]) || (y && !e[y][i]))
ban[i]=T1,ans--;
for(int i=1;i<=B;i++)
if(tim[i]!=T1 && ban[i]!=T1)
{
T2++;
if(Hungary(i)) ans--;
}
return ans;
}
int main()
{
scanf("%d%d%d",&A,&B,&AB);
for(int i=1;i<=A;i++) scanf("%d",&a[i]);
for(int i=1;i<=B;i++) scanf("%d",&b[i]);
for(int i=1;i<=A;i++)
{
if(a[i]&1) odd[++cnto]=i;
else even[++cnte]=i;
}
for(int i=1;i<=B;i++)
for(int j=1;j<=B;j++)
if(Count(b[i],b[j])) edge(i,j);
for(int i=1,x,y;i<=AB;i++)
{
scanf("%d%d",&x,&y);
e[x][y]=true;
}
ans=calc();
for(int i=1;i<=A;i++) ans=Max(calc(i)+1,ans);
for(int i=1;i<=cnto;i++)
for(int j=1;j<=cnte;j++)
ans=Max(calc(odd[i],even[j])+2,ans);
printf("%d\n",ans);
return 0;
}