问题:给定一个集合,枚举所有可能的子集。
一、增量生成
思路:一次选出一个元素放到集合。
void print_subset(int n, int *A, int cur){
for(int i = 0; i < cur; i++) printf("%d ",A[i]);
printf("\n");
int s = cur ? A[cur - 1] : 0; //A[]数组中的元素字典排序,确定当前元素最小可能值
for(int i = s; i < n; i++) {
A[cur] = i;
print_subset(n,A,cur+1); // 递归
}
}
二、位向量法
思路:构造位向量B[i],B[i] = 1 表示 i 在子集合中;B[i] = 0 表示 i 不在子集合中。该问题的解空间是子集树。
void print_subset(int n, int *B, int cur){
if(cur == n){
for(int i = 0; i < n; i++) if(B[i]) printf("%d ",i);
printf("\n");
return;
}
// 选 i
B[i] = 1;
print_subset(n,B,cur+1);
// 不选 i
B[i] = 0;
print_subset(n,B, cur+1);
}
三 、二进制法
思路:用二进制来表示{0,1,2,......,n-1}的子集S:从右往左第 i 位(从0编号)表示元素 i 是否在子集中。例如:下面的二进制位可以表示子集S={0,1,2,4,5,9,10,14}。
二进制位
0 | 1 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 1 |
子集S
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
对于一个n个元素的子集,其空集可以用0表示,即二进制位全为0;全集可以用 2^n-1 表示,即二进制位全为1。
void print_subset(int n, int s){
for(int i = 0; i < n; i++) // 对整数s,s二进制位第i位表示元素i是否在集合中
if(s & (1<<i)) printf("%d ",i); // 如果第i位位1,输出 i;为0,不输出。
printf("\n");
}
for(int i = 0; i < (1<<n); i++) // 子集有 2^n-1
print_subset(n,i);