传送门:点击打开链接
题意:给n和m,还有m个互不相同的数,均小于1<<n,如果m个数字中a & b = 0,那么a 和b有边相连,问有多少个连通分量。
分析:把每个数看做一个01集合,对于在m个数中的数,求它的补集的子集个数即可这里用4位来举个例子,比如5(0101),补集:(1010),补集的子集:(1010) (1000) (0010) (0000)补集的子集和该集合的&运算的结果为0,满足条件。那么就可以枚举0 - ((1<<n) - 1)进行DFS即可。每次找到所有联通的点。时间复杂度O(1<<n)。注意反码和按位取反的区别。反码不需要对符号位取反,按位取反是对所有位取反。计算机内部在做数学运算时(也就是计算机的0和1的运算),都是以补码为标准的,说白了 计算机中就一种码那就是补码,而现实社会中的编码规则,例如原码、反码都是我们自定义的,为了和计算机中的补码形成转换关系。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,vis[1<<23],ct[1<<23];
void dfs(int x) {
if(vis[x]) return ;
vis[x]=1;
for(int i=0;i<n;i++)
if( x & (1<<i) )
dfs( x^(1<<i));
if(ct[x]) dfs( ((1<<n)-1) & (~x));
}
int main() {
cin>>n>>m;
for(int i=0;i<m;i++) {
int num;
cin>>num;
ct[num]=1;
}
int ans=0;
for(int i=0;i<(1<<n);i++)
if(ct[i] && !vis[i])
dfs( ((1<<n)-1) & (~i)),ans++;
cout<<ans<<endl;
}