bzoj2744 [HEOI2012]朋友圈——二分图匹配

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2744

首先,求一个图的最大团等价于求它的补图的最大独立集,而二分图的最大独立集 = 总点数 - 最大匹配数;

所以先把图转化成补图,也就是A国奇、偶点各自成团,B国奇、偶点相互连边而其内部无边,还有些A到B的边用邻接矩阵存了;

可以发现A国最多只能选出两个点来,而A国选的那些点会影响B国的最大独立集;

发现A国点很少,不妨暴力枚举!

注意计算最大独立集时减去的数 tmp,原来写的是 tmp = 最大匹配数,其中左部点(B国奇数点)不管,右部点和A国点相连的也匹配;

想的是右部点中和A国相连的就算匹配上了也会算在减去的部分,但这样不太对呢,而且会少算左部点中和A国相连的;

所以直接再来一个 out 数组表示和A国点相连,把B国中左右部的 out 点都算上,然后匹配时不去匹配 out 的右部点;

虽然感觉挺麻烦但其实也挺好写的。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
int const maxn=3005;
int A,B,m,a[205],b[maxn],pre[maxn],ans,tmp,sid[205][maxn];
bool vis[maxn],out[maxn];
vector<int>vb[maxn];
bool dfs(int x)
{
    for(int i=0;i<vb[x].size();i++)
    {
        int u=vb[x][i];
        if(vis[u]||out[u])continue; vis[u]=1;
        if(!pre[u]||dfs(pre[u])){pre[u]=x;return 1;}
    }
    return 0;
}
bool ck(int x,int y)
{
    int k=(x|y),ret=0;
    for(;k;k-=(k&-k))ret++;
    return (ret&1);
}
int main()
{
    scanf("%d%d%d",&A,&B,&m);
    for(int i=1;i<=A;i++)
    {
        scanf("%d",&a[i]);
//        if(a[i]&1)a1.push_back(i);
//        else a2.push_back(i);
    }
    for(int i=1;i<=B;i++)
    {
        scanf("%d",&b[i]);
        for(int j=1;j<i;j++)
        {
            if(ck(b[i],b[j])||(b[i]^b[j])%2==0)continue;//补图上无边 
            vb[i].push_back(j),vb[j].push_back(i);
        }    
    }
    for(int i=1,x,y;i<=m;i++){scanf("%d%d",&x,&y); sid[x][y]=1;}
    for(int j=1;j<=B;j++)
    {
        if(b[j]%2==0)continue;//不dfs右部点 
        memset(vis,0,sizeof vis);
        if(dfs(j))ans++;
    }
    ans=B-ans;
    for(int i=1;i<=A;i++)
    {
        tmp=0;
        memset(pre,0,sizeof pre);
        memset(out,0,sizeof out);
        for(int j=1;j<=B;j++)if(!sid[i][j])out[j]=1,tmp++;//补图上有边 
        for(int j=1;j<=B;j++)
        {
            if(b[j]%2==0||out[j])continue;
//            if(!sid[i][j]){tmp++; continue;}
            memset(vis,0,sizeof vis);
            if(dfs(j))tmp++;
        }
        tmp=B-tmp+1;
        ans=max(ans,tmp);
    }
    for(int i=1;i<=A;i++)
    {
//        int i=a1[ii];
        if(a[i]%2==0)continue;
        for(int j=1;j<=A;j++)
        {
            if(a[j]%2==1)continue;
//            int j=a2[jj]; 
            tmp=0;
            memset(pre,0,sizeof pre);
            memset(out,0,sizeof out);
            for(int k=1;k<=B;k++)if(!sid[i][j]||!sid[j][k])out[j]=1,tmp++;//!
            for(int k=1;k<=B;k++)
            {
                if(b[k]%2==0||out[k])continue;//补图上有边 
                memset(vis,0,sizeof vis);
                if(dfs(k))tmp++;
            }
            tmp=B-tmp+2;
            ans=max(ans,tmp);
        }
    }
    printf("%d\n",ans);
    return 0;
}

 

posted @ 2018-07-08 18:45  Zinn  阅读(156)  评论(0编辑  收藏  举报