洛谷P1157題解
阿新 • • 發佈:2021-07-13
組合數的輸出
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;
}