传送门:点击打开链接
题意:给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; }