题目
给定n个元素,问这n个元素组成的每一个集合的所有子集。(不妨n<=15)
思路来源
https://www.cnblogs.com/MyNameIsPc/p/8876855.html
https://www.jianshu.com/p/cfe562dcf2f6(反证法证明 枚举的是全部子集)
代码
for (int S=1; S<(1<<n); ++S){
for (int S0=S; S0; S0=(S0-1)&S)
//do something.
}
考虑S的子集,在二进制上从大到小排成一排,那么大的通过减若干个1就一定能到小的,
但是中间会产生大量的状态,这些状态中包含了一些S中不包含的1,故和S与一下,去冗即可,
从而每两个相邻的状态就都是S的子集,由于降序从而任意两个状态不重复,即任意子集状态均可达
注意到如果是S0-=lowbit(S0)操作的话,应该写作S0=(S0-1)&S0,每次少一个最右的1
而这里的区别是S0=(S0-1)&S,能起到枚举所有子集的作用,递减序枚举
暴力两层S<(1<<n)与S0<(1<<n)的复杂度是,而这样做的复杂度是所有元素个数,
如果不包括S自身,则先变小一步即可
for (int S=1; S<(1<<n); ++S){
for (int S0=(S-1)&S; S0; S0=(S0-1)&S)
//do something.
}
心得
18CCPC秦皇岛需要用到这个,然而当时不会
19南昌邀请赛斯坦纳树用到了这个,然而看了cls的代码也看不懂
后来发现,这个东西叫做枚举集合的子集,感觉好神奇……
复杂度
n位选出k位的方案数有个,k位的子集个数有
个
所有集合的子集的元素个数和为 (不妨k=0也计算在内)
即,二项式定理知为