1. 程式人生 > 其它 >洛谷P1157題解

洛谷P1157題解

組合數的輸出

https://www.luogu.com.cn/problem/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;
}