method 1 增量构造法
思路: 一次选出一个元素放到集合中
代码:
//A表示原集合
//B表示子集和
//N表示原集合个数
//cur表示当前子集和个数
#include<cstdio>
using namespace std;
void print_subset(int *A, int *B, int N, int cur){
for(int i = 0; i < cur; i++)
printf("%3d",B[i]);
printf("\n");
if(cur < N){
for(int i = 0; i < N; i++){
if(!cur || A[i] >B[cur-1]){
B[cur] = A[i];
print_subset(A, B, N, cur+1);
}
}
}
}
int main(){
int L = 3;
int A[L] = {1, 2, 3};
int B[L] = {0};
print_subset(A, B, L, 0);
printf("\n");
return 0;
}
method 2 位向量法
思路: 构造一个位向量B[i], 而不是直接构造子集A本身, 其中B[i] = 1 , 当且仅当 i 在子集 A 中
即 1个容量为N的集合,每个位置0~N-1,对于每个子集,要么被选中,要么没被选中。枚举每一个位置的状态,可得到各种子集。
递归实现
#include<cstdio>
using namespace std;
int B[100];
void print_subset(int n,int *B,int cur)
{
if(cur==n)
{
for(int i=0;i<cur;i++)
if(B[i])printf("%d ",i);
printf("\n");
return ;
}
B[cur]=1;
print_subset(n,B,cur+1);//选
B[cur]=0;
print_subset(n,B,cur+1);//不选
}
int main()
{
int n;scanf("%d",&n);
print_subset(n,B,0);
}
method 3 二进制法
类似于位向量法,同样也是枚举各个位置的状态,但这次用二进制表示,二进制长度为N,与原集合大小相同。二进制的第 i 位代表原集合中的第 i 位是否被选中,枚举各种情况。集合大小为N,就是2的N次种方式。
/*算法思想
例如求4个元素 3 2 1 0 的子集。
那么用二进制的1代表每一位是否选中。
十进制 二进制
0 0000 代表空集
1 0001 代表{0}
2 0010 代表{1}
3 0011 代表{0,1}
4 0100 代表{2}
...
15 1110 代表{3,2,1}
16 1111 代表{3,2,1,0}
如果n很大的话可以用字符串模拟二进制
*/
# include <stdio.h>
# include <algorithm>
using namespace std;
//二进制法求子集
void print_subset(int n,int s){
for(int i=0;i<n;i++){
if(s & (1<<i)) //1左移i位,监测s的哪一位为1,为1的话输出
printf("%d ",i);
}
printf("\n");
}
int main() {
int n=3;
for(int i=0;i<(1<<n);i++){//1左移n位等价于2^n-1.因为子集个数2^n-1
print_subset(n,i);
}
return 0;
}