POJ-3126___Prime Path——DFS+思维

题目大意:

    给你 m m 个数,每个数都不超过 2 n 2^n ,对这 m m 个数,若 x x & y = 0 y=0 ,则 x x y y 属于一个连通集,问一共有几个连通集???

解题思路:

    显然,暴力是超时的,考虑到与操作,对于 x x & y = 0 y=0 ,若我们先把所有& x = 0 x=0 的数先求出来,若后面的数与我们之前求出来的数相等,则说明他们不属于一个连通集
    因此,若 i i & x = 0 x=0 ,那么 i = x i=x ^ ( 1 < < n ) 1 (1<<n)-1 ,同时,若将i的二进制中的 1 1 改为 0 0 ,也满足& x = 0 x=0 ,那么就要求i的所有子集,就用到DFS

代码思路:

    一开始我只是先求 i i ,然后对 i i DFS,遍历的时候直接判断,发现答案多了很多连通集,细细思考后发现有蹊跷,这里贴出两份代码,第二份是考虑到了后续的 a [ i ] a[i] 的补集被忽略掉了的正确代码,直接看代码容易理解

核心:思维、从位运算中转化到DFS,有点东西

第一份错误代码:

#include<bits/stdc++.h>
using namespace std;
int n, m, cnt, all;
bool vis[1<<23];
int a[1<<23];

void DFS(int k) {
	vis[k] = true;
	for(int i=0; i<n; i++) {
		if(k & (1<<i)) {
			int v = k ^ (1<<i);
			if(!vis[v]) DFS(v);
		}
	}
}

int main() {
	scanf("%d%d", &n, &m);
	all = (1<<n) - 1;
	for(int i=1; i<=m; i++)
		scanf("%d", a+i);
	for(int i=1; i<=m; i++) {
		if(!vis[ a[i] ])
			cnt++;
		DFS(all^a[i]);
	}
	printf("%d", cnt);
}

第二份正确代码:

#include<bits/stdc++.h>
using namespace std;
int n, m, cnt, all;
bool vis[1<<23], have[1<<23];
int a[1<<23];

void DFS(int k) {
	vis[k] = true;
	if(!vis[k ^ all] && have[k])
		DFS(k ^ all);
	for(int i=0; i<n; i++) {
		if(k & (1<<i)) {
			int v = k ^ (1<<i);
			if(!vis[v]) DFS(v);
		}
	}
}

int main() {
	scanf("%d%d", &n, &m);
	all = (1<<n) - 1;
	for(int i=1; i<=m; i++) {
		scanf("%d", a+i);
		have[a[i]] = true;
	}
	for(int i=1; i<=m; i++) {
		if(!vis[ a[i] ]) {
			cnt++;
			DFS(all^a[i]);
		}
	}
	printf("%d", cnt);
}

猜你喜欢

转载自blog.csdn.net/Scar_Halo/article/details/83050749