题目大意:
给你 个数,每个数都不超过 ,对这 个数,若 & ,则 与 属于一个连通集,问一共有几个连通集???
解题思路:
显然,暴力是超时的,考虑到与操作,对于
&
,若我们先把所有&
的数先求出来,若后面的数与我们之前求出来的数相等,则说明他们不属于一个连通集
因此,若
&
,那么
^
,同时,若将i的二进制中的
改为
,也满足&
,那么就要求i的所有子集,就用到DFS
代码思路:
一开始我只是先求 ,然后对 DFS,遍历的时候直接判断,发现答案多了很多连通集,细细思考后发现有蹊跷,这里贴出两份代码,第二份是考虑到了后续的 的补集被忽略掉了的正确代码,直接看代码容易理解
核心:思维、从位运算中转化到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);
}