CodeForces 986C AND Graph

题目链接

(逻辑运算+思维+dfs)

题意:第一行给出一个n和m,随后一行是m个数据,数据范围是0~(1<<n)(即2^n),现在假定这些数都是一个点,当两个数进行逻辑&运算=0时,他们之间存在连边,问这m个数组成的图存在几个联通集合?

分析:首先我们要分析一下,对于一个数X可以连接的点有什么,因为是进行逻辑&运算,因此对于一个数在(1<<n)的范围内能连边的点的值即为:将X在(1<<n)范围内取反得到最大值Y(这是X在范围内进行&运算=0的最大值),那么若是我们将Y中二进制位的任意1变成0得到Z,这时有X&Z=0,即他们存在联边,若这个Z是给出的m个数中的一个,那么我们可以将Z这个数dfs重复上面的方法,找到从X出发的所有连接的点。

题解:那么我们要考虑的是如何更快的而且可以准确的dfs一个点将与他相连接的点全部标记呢,有点像贪心吧,我们可以选择给出的m个数从小到大来dfs,每次dfs将这个集合中的所有点标记起来,并且集合数++,之后依次dfs没有被标记过的数即可。

(代码中采用的是从0到(1<<n)的一个for循环,原理一样,都是在遇到给出的数a[i]=1时才dfs(i ))

代码如下:

#include<iostream>
#include<cmath>
#include<string>
#include<cstring>
#include<cstdio>
#include<time.h>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
const int maxn = 1 << 23;
bool a[maxn];//用于记录哪个是给出的数
bool vis[maxn];
int n, m, x;
void dfs(int x) {
	if (vis[x])return;
	vis[x] = true;
	for (int i = 0; i <= n; i++)
		if (x&(1 << i))//二进制第i+1位为1
			dfs(x ^ (1 << i));
	if (a[x]) dfs((1 << n) - 1 - x);//获得这个数在(1<<n)范围内取反的值
}
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 0; i < m; i++)
		scanf("%d", &x), a[x] = true;
	int ans = 0;
	for (int i = 0; i < (1 << n); i++)//从小到大dfs给输出的m个数
		if (!vis[i] && a[i])
			dfs(i), ans++;
	printf("%d\n", ans);
	return 0;
}



猜你喜欢

转载自blog.csdn.net/weixin_41156591/article/details/80543429
今日推荐