bzoj 2744: [HEOI2012]朋友圈 (二分图最大团)

2744: [HEOI2012]朋友圈

Time Limit: 30 Sec  Memory Limit: 128 MB
Submit: 942  Solved: 296
[ Submit][ Status][ Discuss]

Description

在很久很久以前,曾经有两个国家和睦相处,无忧无虑的生活着。一年一度的评比大会开始了,作为和平的两国,一个朋友圈数量最多的永远都是最值得他人的尊敬,所以现在就是需要你求朋友圈的最大数目。
两个国家看成是AB两国,现在是两个国家的描述:
1.         A国:每个人都有一个友善值,当两个A国人的友善值a、b,如果a xor b mod 2=1,
那么这两个人都是朋友,否则不是;
2.         B国:每个人都有一个友善值,当两个B国人的友善值a、b,如果a xor b mod 2=0
或者 (a or b)化成二进制有奇数个1,那么两个人是朋友,否则不是朋友;
3.         A、B两国之间的人也有可能是朋友,数据中将会给出A、B之间“朋友”的情况。


4.     在AB两国,朋友圈的定义:一个朋友圈集合S,满足

S∈A∪ B                  ,对于所有的i,j∈  S ,i 和       j   是朋友

由于落后的古代,没有电脑这个也就成了每年最大的难题,而你能帮他们求出最大朋 友圈的人数吗?

Input

 

第一行t<=6,表示输入数据总数。
接下来t个数据:
第一行输入三个整数A,B,M,表示A国人数、B国人数、AB两国之间是朋友的对数;第二行A个数ai,表示A国第i个人的友善值;第三行B个数bi,表示B国第j个人的友善值;
第4——3+M行,每行两个整数(i,j),表示第i个A国人和第j个B国人是朋友。

Output

 
输出t行,每行,输出一个整数,表示最大朋友圈的数目。

Sample Input

2 4 7
1 2
2 6 5 4
1 1
1 2
1 3
2 1
2 2
2 3
2 4

Sample Output

5
【样例说明】
最大朋友圈包含A国第1、2人和B国第1、2、3人。

HINT

【数据范围】

两类数据

第一类:|A|<=200 |B| <= 200

第二类:|A| <= 10 |B| <= 3000

 

Source

[ Submit][ Status][ Discuss]


题解:二分图最大团。

这个题让求一个子图满足子图中的点两两之间都有边相连,其实也就是是最大团。

最大独立集是一个子图满足子图中的点两两之间没有边相连。

最大团=补图的最大独立集=顶点数-二分图的最大匹配   注意一定要是二分图。

这道题刚开始的时候直接建图跑的补图的最大匹配,发现这不是二分图必然不对啊。。。。。。

然后开始发现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);
}  



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值