递归实现组合型枚举

Neurocoda

Description

从 1~n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案。,,。

Input

两个整数 n,m。

Output

按照从小到大的顺序输出所有方案,每行 1 个。首先,同一行内的数升序排列,相邻两个数用一个空格隔开。其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面(例如 1 3 9 12 排在 1 3 10 11 前面)。

Sample Input

1
5 3

Sample Output

1
2
3
4
5
6
7
8
9
10
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5

Limit

Time LimitMemory Limit
1 秒(C/C++/Rust/Pascal),2 秒(其他语言)32 M(C/C++/Rust/Pascal),64 M(其他语言)

Analysis

同 CH0301 递归实现指数型枚举,不过本题需要分析一下数据范围:
由题目条件,,:

再结合,对固定的,有

要让的取值区间非空,必须,再结合,得到。分段写出:

总之,若均为整数,数据范围可总结为:

其中最大为,最大也为(此时)。

补充,为了表达的方便,本文在之后的时间复杂度分析中参数会用代替

Solve

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>

const int N = 30;

int n, m, st[N];
int ans[N], idx;

void dfs(int p, int cnt) {
if(cnt == m)
for(int i = 0; i < idx; ++i)
std::cout << ans[i] << ' ';

if(cnt == m) std::cout << '\n';
for(int i = p + 1; i <= n; ++i) {
if(!st[i]) {
st[i] = 1;
ans[idx++] = i;
dfs(i, cnt + 1);
st[i] = 0;
--idx;
}
}
}

int main () {
std::ios::sync_with_stdio(0);
std::cin.tie(0);

std::cin >> n >> m;
for(int i = 1; i <= n; ++i) {
st[i] = 1;
ans[idx++] = i;
dfs(i, 1);
st[i] = 0;
--idx;
}
}

时间复杂度依然为

Ex

如果要求使用非递归方法,该怎么做呢?

嗯,因为最大为,最大也为,可以想到状态压缩的思路:
枚举从到的状态,对每个状态,根据技巧快速判断其中的的数目是否为,若是,则按顺序遍历该状态的每一个比特,当比特位为一时输出对应序号。时间复杂度依然为,代码实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>

const int N = 25;

int n, m;

int getBitNum(int i) {
int cnt = 0;
while(i) {
++cnt;
i ^= i & (-i);
}
return cnt;
}

void display(int x) {
for(int i = 0; i < N; ++i) {
if(x & 1 << i) std::cout << i + 1 << ' ';
}
std::cout << '\n';
}

int main () {
std::ios::sync_with_stdio(0);
std::cin.tie(0);

std::cin >> n >> m;
for(int i = 0; i < 1 << n; ++i) {
if(getBitNum(i) == m) {
display(i);
}
}
}

但是这个代码实现不能通过题目,因为题目有输出顺序要求「按照从小到大的顺序输出所有方案」,一个简单的想法是额外空间存储这些符合条件的状态,对这些状态自定义排序(即),最后再统一输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <algorithm>

const int N = 25;

int n, m;
int st[1 << N], idx;

int getBitNum(int i) {
int cnt = 0;
while(i) {
++cnt;
i ^= i & (-i);
}
return cnt;
}

void display(int x) {
for(int i = 0; i < N; ++i) {
if(x & 1 << i) std::cout << i + 1 << ' ';
}
std::cout << '\n';
}

int main () {
std::ios::sync_with_stdio(0);
std::cin.tie(0);

std::cin >> n >> m;
for(int i = 0; i < 1 << n; ++i) {
if(getBitNum(i) == m) {
// display(i);
st[idx++] = i;
}
}
std::sort(st, st + idx, [](int a, int b) {
for (int j = 0; j < n; ++j)
if (((a >> j) & 1) != ((b >> j) & 1)) return (a >> j) & 1;
return 0;
});
for(int i = 0; i < idx; ++i) display(st[i]);
return 0;
}

其中:

1
2
3
4
5
6
for(int i = 0; i < 1 << n; ++i) {
if(getBitNum(i) == m) {
// display(i);
st[idx++] = i;
}
}

这个部分是

1
2
3
4
5
std::sort(st, st + idx, [](int a, int b) {
for (int j = 0; j < n; ++j)
if (((a >> j) & 1) != ((b >> j) & 1)) return (a >> j) & 1;
return 0;
});

这个部分是

1
for(int i = 0; i < idx; ++i) display(st[i]);

这个部分是

综上,代码的时间复杂度为

Ref

  • Title: 递归实现组合型枚举
  • Author: Neurocoda
  • Created at : 2026-09-29 09:32:46
  • Updated at : 2026-09-29 12:20:02
  • Link: https://neurocoda.com/p/7f749e0d.html
  • License: This work is licensed under CC BY-ND 4.0.