题意:给你N个点,让你求一个最大集合,使得集合内任意两个数字在二进制下都有两个或两个以上的位置不同。
题解:
- 首先明白 二分图的最大独立集问题和最大团问题是互补的关系。
- “让你求一个最大集合,使得集合内任意两个数字在二进制下都有两个或两个以上的位置不同”这就可以看成是最大团问题。
- 所以 相反考虑的话 就是求 “两个数字在二进制下只有一位不同” 的 问题, 那么最后这两数字肯定只取一个。就可以转换成二分图最大匹配了。进而考虑最大独立集。 【建图思想】
- 二分图最大独立集=N-二分图最大匹配(实质是最大匹配的边)
- 最小点覆盖的点集=最大匹配数
- 至于建图,把两点二进制只有一位1不同的两点分别放在左集和右集中,那么最后这两侧匹配的数字肯定只取一侧的,左集右集也符合二分图建图。
- 细节就是把奇数1的放在左集,偶数1的放在右集。
- 最小点覆盖覆盖到的点的计算方法:最终左边未被标记的点,与右边被标记的点就是覆盖的点集 (恰好是每条匹配边的一个端点) 如果是拿dinic分层跑的话,左部的点就是dis[]为-1的点,右部就是dis[]不为-1的点。 最大独立集的点与最小点覆盖恰好相反,也就是最小点覆盖的补图就是最大独立集。
第二问我是dlc网络流跑二分图,所以最大独立集的点就等于 左部被标记的点(dis[]!=-1)和右部未被标记的点(dis[]=1)
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 5e3+5;//点
const int N = 3e7+5;//边
const int INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
int a[maxn];
int oddvis[maxn];
bool vis[maxn];
struct Edge{
int to;
int cap, flow;
int next;
}edge[N];
int cnt;
int head[maxn];
int dis[maxn];
int cur[maxn];
void add(int u, int v, int w){
edge[cnt] = (struct Edge){v, w, 0, head[u]};
head[u] = cnt++;
edge[cnt] = (struct Edge){u, 0, 0, head[v]};
head[v] = cnt++;
}
bool bfs(int start, int endd){//分层
memset(dis, -1, sizeof(dis));
memset(vis, false, sizeof(vis));
queue<int>que;
dis[start] = 0;
vis[start] = true;
que.push(start);
while(!que.empty()){
int u = que.front();
que.pop();
for(int i = head[u]; i != -1; i = edge[i].next){
Edge E = edge[i];
if(!vis[E.to] && E.flow<E.cap){
dis[E.to] = dis[u]+1;
vis[E.to] = true;
if(E.to == endd) return true;
que.push(E.to);
}
}
}
return false;
}
int dfs(int x, int res, int endd){ //增广
if(x == endd || res == 0) return res;
int flow = 0, f;
for(int& i = cur[x]; i != -1; i = edge[i].next){
Edge E = edge[i];
if(dis[E.to] == dis[x]+1){
f = dfs(E.to, min(res, E.cap-E.flow), endd);
if(f>0){
edge[i].flow += f;
edge[i^1].flow -= f;
flow += f;
res -= f;
if(res == 0) break;
}
}
}
return flow;
}
int max_flow(int start, int endd){
int flow = 0;
while(bfs(start, endd)){
memcpy(cur, head, sizeof(head));
flow += dfs(start, INF, endd);
}
return flow;
}
void init(){//初始化
cnt = 0;
memset(head, -1, sizeof(head));
}
int getlen(int x){
int len=0;//1的个数
while(x){
// if(x%2) len++;
//x=x/2;
len++;
x-=(x&-x);
}
return len;
}
int main(){
std::ios::sync_with_stdio(false);
int n;
cin>>n;
int s=0;//建边 左奇右偶
int e=n+1;
init();
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
int tmp=a[i];
int len=getlen(tmp);
if(len&1) oddvis[i]=1,add(s,i,1);
else add(i,e,1);
}
for(int i=1;i<=n;i++){
int na=a[i];
for(int j=i+1;j<=n;j++){
int nb=a[j];
int nab=na^nb;//找两数相差只有一个1的
if(getlen(nab)==1){
if(oddvis[i]) add(i,j,1);
else add(j,i,1);
}
}
}
int num=max_flow(s,e);//二分图最大匹配
cout<<n-num<<endl;//二分图最大独立集
for(int i=1;i<=n;i++)//最大独立集的点
if(oddvis[i]&&dis[i]!=-1) cout<<a[i]<<" ";
else if(oddvis[i]==0&&dis[i]==-1) cout<<a[i]<<" ";
cout<<endl;
return 0;
}