• 2019牛客多校第五场F maximum clique 1 最大独立集


    题意:给你n个数,现在让你选择一个数目最大的集合,使得集合中任意两个数的二进制表示至少有两位不同,问这个集合最大是多大?并且输出具体方案。保证n个数互不相同。

    思路:容易发现,如果两个数不能同时在集合中,这两个数的二进制表示一定只有一位不同(因为n个数互不相同,所以一定不会有两个数的二进制位一定相同)。那么我们不妨把每个数和它只有一位不同的数连一条边,那么原问题就变成了在一张图上找最多的点,使得任意两点间都没有变直接相连,而这个问题就是最大独立集问题。而且,由于n个数互不相同,所以这张图一定没有长度为奇数的环,即这张图一定是二分图。那么,现在问题就变成了求一张二分图的最大独立集大小并输出具体方案。独立集大小我们都知道是n - 最大匹配数,怎么求具体方案呢?我们知道最大独立集和最小点覆盖是互补关系,那么我们可以求出最小点覆盖,不是最小点覆盖的点就是最大独立集中的点了。

    最小点覆盖的构造方法如下(来自《算法竞赛进阶指南》):

    1:求出二分图的最大匹配。

    2:从左部每个非匹配点出发,再执行一次dfs寻找增广路的过程(这个过程一定会失败),并标记沿途访问过的节点。

    3:取左部未被标记过的节点,右部标记过的节点,就得到了二分图的最小点覆盖。

    对于这个题,在构造出了图之后,我们先通过搜索确定哪些点在左边,哪些点在右边,之后执行二分图最大匹配。最后,枚举点,如果这个点没有点和它匹配(即非匹配点),从这个点再进行增广。最后把不是最小点覆盖中的点计入答案。

    代码:

    #include <bits/stdc++.h>
    #define pii pair<int, int>
    using namespace std;
    const int maxn = 5010;
    int a[maxn];
    bool v[maxn], l[maxn];
    bool va[maxn], vb[maxn];
    vector<int> rea, reb;
    int match[maxn];
    vector<int> G[maxn], ans;
    void add(int x, int y) {
    	G[x].push_back(y);
    	G[y].push_back(x);
    }
    bool dfs(int x) {
    	va[x] = 1;
    	v[x] = 1;
    	for (int i = 0; i < G[x].size(); i++) {
    		int y = G[x][i];
    		if(!vb[y]) {
    			vb[y] = 1;
    			if(!match[y] || dfs(match[y])) {
    				match[y] = x;
    				return 1;
    			}
    			v[y] = 1;
    		}
    	}
    	return 0;
    }
    void dfs1(int x, int dep) {
    	if(dep & 1) {
    		rea.push_back(x);
    		l[x] = 1;
     	} else {
    		reb.push_back(x);
    		l[x] = 0;
    	}
    	v[x] = 1;
    	for (int i = 0; i < G[x].size(); i++) {
    		int y = G[x][i];
    		if(v[y]) continue;
    		dfs1(y, dep + 1);
    	}
    }
    int main() {
    	int n;
    	//freopen("in.txt", "r", stdin);
    	scanf("%d", &n);
    	for (int i = 1; i <= n; i++)
    		scanf("%d", &a[i]);
    	for (int i = 1; i <= n; i++)
    		for (int j = i + 1; j <= n; j++) {
    			if(__builtin_popcount(a[i] ^ a[j]) == 1) {
    				add(i, j);
    			}
    		}
    	for (int i = 1; i <= n; i++) {
    		if(v[i]) continue;
    		dfs1(i, 1);
    	}
    	memset(v, 0, sizeof(v));
    	if(rea.size() > reb.size()) {
    		swap(rea, reb);
    		for (int i = 1; i <= n; i++)
    			l[i] ^= 1;
    	}
    	for (int i = 0; i < rea.size(); i++) {
    		memset(va, 0, sizeof(va));
    		memset(vb, 0, sizeof(vb));
    		dfs(rea[i]);
    	}
    	memset(va, 0, sizeof(va));
    	memset(vb, 0, sizeof(vb));
    	memset(v, 0, sizeof(v));
    	for (int i = 0; i < rea.size(); i++) {
    		bool flag = 0;
    		int x = rea[i];
    		for (int j = 0; j < G[x].size(); j++) {
    			int y = G[x][j];
    			if(match[y] == x) {
    				flag = 1;
    				break;
    			}
    		}
    		if(flag == 0 && v[x] == 0) {
    			dfs(x);
    		}
    	}
    	for (int i = 1; i <= n; i++) {
    		if(l[i] == 1 && v[i] == 1) ans.push_back(i);
    		else if(l[i] == 0 && v[i] == 0) ans.push_back(i);
    	}
    	printf("%d
    ", ans.size());
    	for (int i = 0; i < ans.size(); i++) {
    		printf("%d ", a[ans[i]]);
    	}
    	printf("
    ");
    }
    
  • 相关阅读:
    PAT (Advanced Level) Practise:1008. Elevator
    练习题-二维数组中的查找
    PAT (Basic Level) Practise:1028. 人口普查
    PAT (Basic Level) Practise:1014. 福尔摩斯的约会
    PAT (Basic Level) Practise:1019. 数字黑洞
    c++ 二进制文件读写
    c/c++ linux/windows 读取目录下的所有文件名
    C 语言实现 Linux touch 命令
    c++读写csv
    linux nohup【转】
  • 原文地址:https://www.cnblogs.com/pkgunboat/p/11286208.html
Copyright © 2020-2023  润新知