1. 程式人生 > >bzoj2744 [HEOI2012]朋友圈 二分圖大匹配——最大獨立集

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; }