bzoj2744 [HEOI2012]朋友圈 二分圖大匹配——最大獨立集
阿新 • • 發佈:2019-01-23
題目描述:
在很久很久以前,曾經有兩個國家和睦相處,無憂無慮的生活著。一年一度的評比大會開始了,作為和平的兩國,一個朋友圈數量最多的永遠都是最值得他人的尊敬,所以現在就是需要你求朋友圈的最大數目。
兩個國家看成是AB兩國,現在是兩個國家的描述:
- A國:每個人都有一個友善值,當兩個A國人的友善值a、b,如果a xor b mod 2=1,
那麼這兩個人都是朋友,否則不是; - B國:每個人都有一個友善值,當兩個B國人的友善值a、b,如果a xor b mod 2=0
或者 (a or b)化成二進位制有奇數個1,那麼兩個人是朋友,否則不是朋友; - A、B兩國之間的人也有可能是朋友,資料中將會給出A、B之間“朋友”的情況。
- 在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;
}