2744: [HEOI2012]朋友圈
Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 942 Solved: 296
[ Submit][ Status][ Discuss]
Description
S∈A∪ B ,对于所有的i,j∈ S ,i 和 j 是朋友
由于落后的古代,没有电脑这个也就成了每年最大的难题,而你能帮他们求出最大朋 友圈的人数吗?
Input
Output
Sample Input
1 2
2 6 5 4
1 1
1 2
1 3
2 1
2 2
2 3
2 4
Sample Output
【样例说明】
最大朋友圈包含A国第1、2人和B国第1、2、3人。
HINT
【数据范围】
两类数据
第一类:|A|<=200 |B| <= 200
第二类:|A| <= 10 |B| <= 3000
Source
题解:二分图最大团。
这个题让求一个子图满足子图中的点两两之间都有边相连,其实也就是是最大团。
最大独立集是一个子图满足子图中的点两两之间没有边相连。
最大团=补图的最大独立集=顶点数-二分图的最大匹配 注意一定要是二分图。
这道题刚开始的时候直接建图跑的补图的最大匹配,发现这不是二分图必然不对啊。。。。。。
然后开始发现a国的人最多只有两个,为什么呢?注意看条件a xor b mod 2=1其实就是说明a,b的二进制最低位为1,也就是两个数的奇偶性不同,那么因为要求是i,j相互认识,所以i与j,k认识那么j,k奇偶性相同,必然不可能相互认识。
所以我们可以枚举A国的选择,然后依照读入的A,B两国的关系建立补图。
我们先不管A国对B国的限制,只是考虑B国,因为a xor b mod 2=0所以显然奇偶性相同的点一定没有边相连,那么就可以将其根据奇偶性建立一个二分图,然后将不满如(a or b)化成二进制有奇数个1的两个点连边(注意这已经是补图了),但是还有A对B的限制,所以与选中的A国人无关的B国人不在二分图中计算答案的时候需要减去。
每次清数组,重建图太麻烦了,我们可以巧妙的利用时间戳,刚开始就把B国的图建好,然后每次对节点打标记,在做匈牙利算法的时候直接跳过标记点。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstring>
#include<bitset>
#define N 3003
using namespace std;
int n,m,map[N][N],k,a[N],b[N],tot;
int point[N],next[N*N],v[N*N],cur[N],belong[N],sz;
int vis[N],ti;
bool pd(int x,int y)
{
bitset<50> now=x|y;
if (now.count()%2) return true;
return false;
}
void add(int x,int y)
{
tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y;
}
bool find(int x,int k)
{
for (int i=point[x];i;i=next[i])
{
if (cur[v[i]]==k||vis[v[i]]==ti) continue;
cur[v[i]]=k;
if (!belong[v[i]]||find(belong[v[i]],k)){
belong[v[i]]=x;
return true;
}
}
return false;
}
int solve()
{
memset(belong,0,sizeof(belong));
int t=0;
for (int i=1;i<=m;i++)
if (vis[i]!=ti&&b[i]&1)
if (find(i,++sz)) t++;
return m-t;
}
int main()
{
freopen("a.in","r",stdin);
scanf("%d%d%d",&n,&m,&k);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=1;i<=m;i++) scanf("%d",&b[i]);
for (int i=1;i<=k;i++) {
int x,y; scanf("%d%d",&x,&y);
map[x][y]=1;
}
memset(vis,-1,sizeof(vis));
int ans=0; //最大团=补图的最大独立集
for (int i=1;i<=m;i++)
if (b[i]&1)
for (int j=1;j<=m;j++)
if (i!=j&&b[j]%2==0&&!pd(b[i],b[j])) add(i,j);
ans=max(ans,solve()); //cout<<ans<<endl;
for (int i=1;i<=n;i++) {
int size=0; ++ti;
for (int j=1;j<=m;j++) if (!map[i][j]) size++,vis[j]=ti;
ans=max(ans,solve()-size+1);
}
for (int i=1;i<=n-1;i++)
for (int l=i+1;l<=n;l++)
if ((a[i]^a[l])%2==1) {
int size=0; ++ti;
for (int j=1;j<=m;j++) if (!map[i][j]||!map[l][j]) size++,vis[j]=ti;
ans=max(ans,solve()-size+2);
}
printf("%d\n",ans);
}