2019牛客暑期多校训练营(第五场) F maximum clique 1 【二分图的最大独立集】

题目描述

You are given a set S containing n distinct positive integers a_1,a_2,...,a_n​.

Please find a subset of S which has the maximum size while satisfying the following constraint:

The binary representations of any two numbers in this subset must have at least two different bits.

If there are multiple valid subsets, please output any one of them.

输入描述 

The input contains only two lines.

The first line contains an integer N denoting the size of S.
The second line contains N positive integers a_1,a_2,...,a_n​ denoting the members of set S.

1\leq N\leq 5000

1\leq a_i\leq 10^9

* all a_i​ are distinct

输出描述 

You should output two lines.

The first line contains one integer m denoting the size of the subset you found.

The second line contains m positive integers denoting the member of this subset.

示例输入

5
3 1 4 2 5

示例输出

3
4 1 2

说明

(11_2) and 1 (01_2) has only 1 different bit so they can not be in the set together. 4 (100_2) and 1 (001_2) has 2 different bits so they can be in the set together.

Following unordered pairs are allowed to be in the set together: <1, 2>, <1, 4>, <2, 4>, <2, 5>, <3, 4>, <3, 5>. Thus the maximum set is of size 3 ({1, 2, 4}).

题目大意:

给定一个集合,要求出最大的一个子集,满足子集中的所有数据两两之间在二进制表示下至少有两位不同。

分析:

根据示例的说明,可以想到在满足条件的两个数据点之间连线,可以构成一个无向图,从而要求的就是该图的最大团。最大团问题和最大独立集问题是互补的,考虑补图的最大独立集。补图的边代表两个数有且仅有一位不同(二进制下1的个数差一)。

可以证明补图是一个二分图,也就是没有奇环:对于一个起点o,假设它存在于一个奇环中,那么直接与o相连的两个数x,y的1的个数要么相同要么相差2(即偶数),假设环的长度是3显然x,y不应该有边相连,与假设矛盾。归纳到任意奇数长度环,最后两个相连的数1的个数相差都为偶数,与假设矛盾。

那么问题转变成求二分图的最大独立集。可以用匈牙利算法求得最大匹配数,最大独立集(个数)=所有顶点数-最大匹配数

这里还要求给出子集中的数,也就是要给出最大独立集的方案。所以在二分图中,对左侧的所有非匹配点,DFS找一次增广路,标记路径上的所有点,那么最后左侧标记的点和右侧没标记的点,就是答案。

具体解释见代码。

#include <bits/stdc++.h>

#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)

using namespace std;

const int maxn = 5005;

int head[maxn];

int ans=0; 
int n;
ll a[maxn];
int col[maxn];
int vis[maxn];
int match[maxn];

struct node{

    int u,v,w;

    int next;

}edge[maxn*maxn];

void add(int u,int v,int w){//向邻接表中加边 

    edge[ans].u=u;

    edge[ans].v=v;

    edge[ans].w=w;

    edge[ans].next=head[u];

    head[u]=ans++;

}

void cdfs(int u, int color){//给二分图染色,左侧的点为 1,右侧的点为 2 
    col[u] = color;
    for(int i=head[u];i!=-1;i=edge[i].next){
    	int t=edge[i].v;
    	if (!col[t])
            cdfs(t, 3 - color);
    }
        
}

bool dfs(int u){//匈牙利算法,尝试寻找增广路 
    vis[u]=1;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(vis[v]) continue;
        vis[v]=1;
        if(!match[v]||dfs(match[v])){
            match[v]=u;
            match[u]=v;
            return true;
        }
    }
    return false;
}

bool ispair(ll a,ll b){//判断两点之间是否连线 
	ll tmp=a^b;
	tmp-=(tmp&-tmp);//这里对两数异或后的数据减去它的lowbit值,若剩下为0,则连线 
	if(tmp>0)  return false;
	else return true;
}

int main()

{

	scanf("%d",&n);
	rep(i,1,n){
		scanf("%lld",&a[i]);
	}
	memset(head,-1,sizeof(head));
	rep(i,1,n){
		rep(j,i+1,n){
			if(ispair(a[i],a[j])){
				add(i,j,1);
				add(j,i,1);
			}
		}
	}
	memset(col,0,sizeof(col));
	//将图划分为左右两部分 
	rep(i,1,n){
		if(col[i]==0) cdfs(i,1);
	}
	//得到最大匹配数 
	int cnt=0;
	rep(i,1,n){
		if(col[i]==1){//左侧点调用DFS 
			memset(vis,0,sizeof(vis));
			if(dfs(i))  cnt++;
		}
	}
	memset(vis,0,sizeof(vis));
	rep(i,1,n){
		if(col[i]==1){
			if(!match[i])  dfs(i);//左侧未匹配的点 
		}
	}
	printf("%d\n",n-cnt);//所有顶点数-最大匹配数
        for(int i=1;i<=n;i++){
            if(col[i]==1&&vis[i]) printf("%d ",a[i]);
            if(col[i]==2&&!vis[i]) printf("%d ",a[i]);
        }
        printf("\n"); 
	return 0;

}
发布了30 篇原创文章 · 获赞 5 · 访问量 900

猜你喜欢

转载自blog.csdn.net/qq_42840665/article/details/98250688