子集生成——增量构造法+位向量法+二进制法
增量构造法:
原理图:
1 // 此算法仅用于输出下标,实际运用应输入另一个数组来进行数据的储存 2 #include <bits/stdc++.h> 3 using namespace std; 4 typedef long long ll; 5 typedef unsigned long long ull; 6 #define INF 0X3f3f3f3f 7 const ll MAXN = 1e3 + 7; 8 const ll MOD = 1e9 + 7; 9 int a[10]; 10 void print_subset(int n,int cur)//cur相当于遍历图的层数 11 { 12 for(int i=0;i<cur;i++) cout<<a[i]; 13 cout<<endl; 14 // 找到当前子集的第一个元素 15 int s=cur?a[cur-1]+1:0;//确定当前元素的最小可能值 16 /* 这一句很难懂,实际上就是相当于下面的代码 17 int s; 18 if(cur==0) s=0; 19 else s=a[cur-1]; 20 */ 21 for(int i=s;i<n;i++) 22 { 23 a[cur]=i; 24 //当前层子集第一个值 25 //cur+1表示当前层值设置完毕,开始递归下一层,和前面步骤一样。 26 //到达最后一层结束后return 到上一层,然后i++,a[cur]的值(首元素)改变,又反复递归下一层 27 print_subset(n,cur+1); 28 } 29 return ; 30 } 31 int main() 32 { 33 print_subset(4,0); 34 system("pause"); 35 return 0; 36 }
位向量法:
构造一个位向量,而不是直接构造子集本身
位向量的解答树如图:
1 // 此算法仅用于输出下标,实际运用应输入另一个数组来进行数据的储存 2 // 在枚举子集的位向量法中,解答树的结点掠夺,但在多数情况下仍然够快 3 #include <bits/stdc++.h> 4 using namespace std; 5 typedef long long ll; 6 typedef unsigned long long ull; 7 #define INF 0X3f3f3f3f 8 const ll MAXN = 1e3 + 7; 9 const ll MOD = 1e9 + 7; 10 int a[10]; 11 void print_subset(int n, int cur) 12 { 13 if (cur == n) 14 { 15 for (int i = 0; i < cur; i++) 16 if (a[i]) cout << i; 17 cout << endl; 18 return; 19 } 20 a[cur]=1;//选第cur个元素 21 print_subset(n,cur+1); 22 a[cur]=0;//不选第cur个元素 23 print_subset(n,cur+1); 24 } 25 int main() 26 { 27 print_subset(4, 0); 28 // system("pause"); 29 return 0; 30 }
二进制法:
还可以用二进制来表示{0,1,2······,n-1}的子集S:从右往左第i位(各位从0开始编号)表示i是否在集合S中,图中展示了二进制0100011000110111是如何表示集合{0,1,2,4,5,9,10,14}的
1 //此算法仅仅是输出下标,实际应用应输入另一个数组来进行存储数据 2 //原理:用数字的二进制位表示状态,二进制从右到左的第几个位置 表示数组元素的下标 3 #include <bits/stdc++.h> 4 using namespace std; 5 typedef long long ll; 6 typedef unsigned long long ull; 7 #define INF 0X3f3f3f3f 8 const ll MAXN = 1e3 + 7; 9 const ll MOD = 1e9 + 7; 10 void print_subset(int n, int s) 11 { 12 //s代表的是当前二进制数表示的状态 13 for(int i = 0; i < n; ++i) 14 if(s & (1 << i)) //1右移几位就代表第i个二进制位为1,其他位为0,与状态进行&运算,如果此状态包含该数字,就输出 15 cout<<i; 16 cout<<endl; 17 } 18 int main() 19 { 20 int n; 21 while(cin>>n) 22 { 23 //一共n个数字,所以其全集有2^n个二进制1,所对应的十进制数字就是2^n - 1 24 //我们要做的就是枚举出来每一种状态,1 右移 n 位,所得的十进制数字就是2^n 25 for(int i = 0; i < (1 << n) ; ++i) //i表示集合元素的状态,根据该状态打印出当前集合 26 print_subset(n, i); 27 } 28 return 0; 29 }