二分图最大独立集模板

//可做网络流和二分图模板

最大独立集是指一个图的点的最大子集,满足任意两点没有边直接相连。另一个相对的概念是最大团,在这个最大的子集中任意两点都有边直接相连,可以发现,补图的最大团就是原图的最大独立集。

在二分图中,|最大独立集|=n-|最大匹配|,这是因为首先有n-2*|最大匹配|个独立点,另外可以证明匹配中每条边可以选入一个点。

用dinic求最大匹配时,最后一次bfs的标记可以用来寻找最大独立集。我们称靠近起点一侧是左侧,另一侧为右侧。level>0的左侧点和level=0的右侧点就是一个最大独立集。这是一种优先选择左侧可行点的选法,不光有在最大匹配中未使用的那些,还有通过左右左“重获新生”的点,也就是,通过偶数次连边到达的左侧点,它保证没有边直接相连,直接相连的那些右侧点都因有效的level值而被pass了。右侧点只选那些不会干扰到这些政策的,即无法到达的那些。这里我们注意,可以到达的右侧点一定是已经使用过(它的出边已被消耗)的,要不然就会形成新的匹配了。

这一题又出现了蜜汁t,经大佬提醒数组开大了就好了。当越界不多时还是有效内存,所以不会报re,坑惨我了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include<bits/stdc++.h>
using namespace std;

const int maxn=5001;

struct edge{
edge(){}
edge(int _to,int _next,int _cap):to(_to),next(_next),cap(_cap){}
int to,next;
int cap;
}es[maxn*27*2]; //每个点最多连27个点,即27个bit各变一次
int head[maxn],cnt;
inline void add(int u,int v,int c){
es[++cnt]=edge(v,head[u],c);
head[u]=cnt;
}

int level[maxn],st,ed;
bool bfs(){
queue<int> q;
memset(level,0,sizeof(level));
q.push(st);
level[st]=1;
while(!q.empty()){
int u=q.front();
q.pop();
for(int v,i=head[u];i;i=es[i].next){
v=es[i].to;
if(!level[v]&&es[i].cap>0){
level[v]=level[u]+1;
q.push(v);
}
}
}
return level[ed]>0;
}

int cur[maxn];
int dfs(int x,int f){
if(x==ed||f==0)
return f;
int res=0,tmp;
for(int &i=cur[x];i;i=es[i].next){
int v=es[i].to;
if(level[v]==level[x]+1&&es[i].cap>0){
tmp=dfs(v,min(f-res,es[i].cap));
es[i].cap-=tmp;es[i^1].cap+=tmp;
res+=tmp;
if(res==f)
break;
}
}
if(!res)
level[x]=0;
return res;
}

int a[maxn];

int n,mark[maxn];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",a+i);
}
cnt=1;
st=n+1,ed=n+2;
for(int i=1;i<n;i++){
for(int j=i+1;j<=n;j++){
if(__builtin_popcount(a[i]^a[j])==1){
if(__builtin_popcount(a[i])&1){
add(i,j,1);add(j,i,0);
}else{
add(j,i,1);add(i,j,0);
}
}
}
}
for(int i=1;i<=n;i++){
if(__builtin_popcount(a[i])%2){
add(st,i,1);add(i,st,0);
}else{
add(i,ed,1);add(ed,i,0);
}
}
int ans=0;
while(bfs()){
for(int i=1;i<=n+2;i++)
cur[i]=head[i];
ans+=dfs(st,5000);
}
cout<<n-ans<<endl;
for(int i=1;i<=n;i++){
if(level[i]){
if(__builtin_popcount(a[i])%2)
printf("%d%c",a[i],i==n?'\n':' ');
}else{
if(__builtin_popcount(a[i])%2==0)
printf("%d%c",a[i],i==n?'\n':' ');
}
}
}