bzoj2744 [HEOI2012]朋友圈 二分圖大匹配——最大獨立集

題目描述:
在很久很久以前,曾經有兩個國家和睦相處,無憂無慮的生活着。一年一度的評比大會開始了,作爲和平的兩國,一個朋友圈數量最多的永遠都是最值得他人的尊敬,所以現在就是需要你求朋友圈的最大數目。
兩個國家看成是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 是朋友

由於落後的古代,沒有電腦這個也就成了每年最大的難題,而你能幫他們求出最大朋 友圈的人數嗎?

題目分析:
求圖的最大團。
最大團等於補圖的最大獨立集。

由於這個問題是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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章