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 << "}
";
        }
    }
    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 << "}
";
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/littlehb/p/15005039.html