[BZOJ]2744: [HEOI2012]朋友圈 二分图匹配

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之间“朋友”的情况。

  1. 在AB两国,朋友圈的定义:一个朋友圈集合S,满足
    S∈A∪ B ,对于所有的i,j∈ S ,i 和 j 是朋友
    由于落后的古代,没有电脑这个也就成了每年最大的难题,而你能帮他们求出最大朋 友圈的人数吗?

题解:

这个题目实际上是要求最大团。如果对最大团的定义不清楚可以点这里。但是这个图是一个一般图,怎么办呢?观察后发现,A国只能选0个人、1个人、2个人(一个奇数一个偶数),而B图的补图又恰好是一个二分图!那就好办了。我们可以枚举A国选了什么人,然后把可选的B国人打上标记,直接跑二分图匹配就好了。记得写初始化啊!

代码:

#include
#include
#include
#include
using namespace std;
const int maxn=3010;
int A,B,M,a[maxn],b[maxn],d[maxn],ch[maxn];
bool h[210][maxn];
struct Edge{int y,next;}e[maxn*maxn];
int last[maxn],len=0;
void ins(int x,int y)
{
    int t=++len;
    e[t].y=y;e[t].next=last[x];last[x]=t;
}
int chw[maxn],match[maxn],T=0,Tc=0;
bool findmatch(int x)
{
    if(ch[x]!=Tc)return false;
    for(int i=last[x];i!=-1;i=e[i].next)
    {
        int y=e[i].y;
        if(chw[y]!=T&&ch[y]==Tc)
        {
            chw[y]=T;
            if(match[y]==-1||findmatch(match[y]))
            {
                match[y]=x;
                return true;
            }
        }
    }
    return false;
}
int count(int x)
{
    int re=0;
    while(x)
    {
        re+=(x&1);
        x>>=1;
    }
    return re;
}
int solve()
{
    memset(match,-1,sizeof(match));
    int re=B;
    for(int i=1;i<=B;i++)
    if(ch[i]==Tc)
    {
        T++;
        if(findmatch(i))re--;
    }
    else re--;
    return re;
}
int main()
{
    memset(ch,0,sizeof(ch));
    memset(last,-1,sizeof(last));
    scanf("%d%d%d",&A,&B,&M);
    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<=M;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        h[x][y]=true;
    }
    for(int i=1;i<=B;i++)
    if(b[i]&1)
    {
        for(int j=1;j<=B;j++)
        if(!(b[j]&1)&&!(count(b[i]|b[j])&1))ins(i,j);
    }
    /*选0个A国人*/
    int ans=solve();
    /*选1个A国人*/
    for(int i=1;i<=A;i++)
    {
        Tc++;
        for(int j=1;j<=B;j++)
        if(h[i][j])ch[j]=Tc;
        ans=max(ans,solve()+1);
    }
    /*选2个A国人*/
    for(int i=1;i<=A;i++)
    if(a[i]&1)
    {
        for(int j=1;j<=A;j++)
        if(!(a[j]&1))
        {
            Tc++;
            for(int k=1;k<=B;k++)
            if(h[i][k]&&h[j][k])ch[k]=Tc;
            ans=max(ans,solve()+2);
        }
    }
    printf("%d",ans);
}

你可能感兴趣的:(二分图)