P1157组合数的输出 题解
组合数的输出
以\(n=5\)为例,那么原集合就是{\(1,2,3,4,5\)}
按照每个数字是否存在于子集合中可能有:
{\(0,0,0,0,0\)} ---->{}
{\(0,0,0,0,1\)} ---->{\(5\)}
{\(0,0,0,1,0\)} ---->{\(4\)}
{\(0,0,0,1,1\)} ---->{\(4,5\)}
{\(0,0,1,0,0\)} ---->{\(3\)}
...
这样的二进制形式,就可以模拟出所有的子集选择情况。
这些二进制,还是可以从0,一直到 31的, 也就是说,我们可以用循环从0遍历到31,就成功模拟了这32种情况,对应着32种子集合。假设我们写出了如下的代码:
for(int i=0;i<31;i++){
...
}
由于数字是从\(0\)到\(31\),所以第一个考查的是0
,第二个考查的是1
,最后一个考查的是31
,对应二进制就是:
数字 | 对应的二进制 | 对应的集合 |
---|---|---|
\(0\) | 0 0 0 0 0 |
{} 空集合 |
\(1\) | 0 0 0 0 1 |
{5} |
\(2\) | 0 0 0 1 0 |
{4} |
\(3\) | 0 0 0 1 1 |
{4,5} |
\(4\) | 0 0 1 0 0 |
{3} |
\(5\) | 0 0 1 0 1 |
{3,5} |
\(6\) | 0 0 1 1 0 |
{3,4} |
\(7\) | 0 0 1 1 1 |
{3,4,5} |
\(8\) | 0 1 0 0 0 |
{2} |
\(9\) | 0 1 0 0 1 |
{2,5} |
\(10\) | 0 1 0 1 0 |
{2,4} |
\(11\) | 0 1 0 1 1 |
{2,4,5} |
\(12\) | 0 1 1 0 0 |
{2,3} |
\(13\) | 0 1 1 0 1 |
{2,3,5} |
\(14\) | 0 1 1 1 0 |
{2,3,4} |
\(15\) | 0 1 1 1 1 |
{2,3,4,5} |
\(16\) | 1 0 0 0 0 |
{1} |
\(17\) | 1 0 0 0 1 |
{1,5} |
\(18\) | 1 0 0 1 0 |
{1,4} |
\(19\) | 1 0 0 1 1 |
{1,4,5} |
\(20\) | 1 0 1 0 0 |
{1,3} |
\(21\) | 1 0 1 0 1 |
{1,3,5} |
\(22\) | 1 0 1 1 0 |
{1,3,4} |
\(23\) | 1 0 1 1 1 |
{1,3,4,5} |
\(24\) | 1 1 0 0 0 |
{1,2} |
\(25\) | 1 1 0 0 1 |
{1,2,5} |
\(26\) | 1 1 0 1 0 |
{1,2,4} |
\(27\) | 1 1 0 1 1 |
{1,2,4,5} |
\(28\) | 1 1 1 0 0 |
{1,2,3} |
\(29\) | 1 1 1 0 1 |
{1,2,3,5} |
\(30\) | 1 1 1 1 0 |
{1,2,3,4} |
\(31\) | 1 1 1 1 1 |
{1,2,3,4,5} |
由于例子是\(r=3\),所以:
数字 | 对应的二进制 | 对应的集合 |
---|---|---|
\(7\) | 0 0 1 1 1 |
{3,4,5} |
\(11\) | 0 1 0 1 1 |
{2,4,5} |
\(13\) | 0 1 1 0 1 |
{2,3,5} |
\(14\) | 0 1 1 1 0 |
{2,3,4} |
\(19\) | 1 0 0 1 1 |
{1,4,5} |
\(21\) | 1 0 1 0 1 |
{1,3,5} |
\(22\) | 1 0 1 1 0 |
{1,3,4} |
\(25\) | 1 1 0 0 1 |
{1,2,5} |
\(26\) | 1 1 0 1 0 |
{1,2,4} |
\(28\) | 1 1 1 0 0 |
{1,2,3} |
#include <bits/stdc++.h>
using namespace std;
const int N = 30;
int a[N];
int main() {
int n, r;
cin >> n >> r;
//如果是正序
for (int S = 0; S <= (1 << n) - 1; S++) {
//每次循环需要清0
memset(a, 0, sizeof a);
int cnt = 0;
for (int i = n - 1; i >= 0; i--) //遍历S的每一个二进制位(从高位到低位)
if (S & (1 << i)) a[cnt++] = i; //记录S的哪些数位不为0
//只需要r位
if (r == cnt) {
cout << S << " {";
/*
数组内容4,对应着数字1
数组内容3,对应着数字2
数组内容2,对应着数字3
数组内容1,对应着数字4
数组内容0,对应着数字5
就是一个两者相加等于n的关系。
*/
for (int i = 0; i < cnt; i++) cout << n - a[i] << " ";
cout << "}\n";
}
}
return 0;
}
大数在前,才能保证 10110 早于 10101.修改一下:
#include <bits/stdc++.h>
using namespace std;
const int N = 30;
int a[N];
int main() {
int n, r;
cin >> n >> r;
//大数在前,才能保证 10110 早于 10101
for (int S = (1 << n) - 1; S >= 0; S--) {
//每次循环需要清0
memset(a, 0, sizeof a);
int cnt = 0;
for (int i = n - 1; i >= 0; i--) //遍历S的每一个二进制位(从高位到低位)
if (S & (1 << i)) a[cnt++] = i; //记录S的哪些数位不为0
//只需要r位
if (r == cnt) {
printf("%3d", S);
cout << " { ";
/*
数组内容4,对应着数字1
数组内容3,对应着数字2
数组内容2,对应着数字3
数组内容1,对应着数字4
数组内容0,对应着数字5
就是一个两者相加等于n的关系。
*/
for (int i = 0; i < cnt; i++) cout << n - a[i] << " ";
cout << "}\n";
}
}
return 0;
}