组合看成严格递增序列,递归逐位选数:递增性天然保证不重不漏,层内从小到大枚举即得字典序。
OJ: roj
题目 ID: 1317
难度:入门
标签:递归枚举python
创建: 2026-09-30 04:53
更新: 2026-10-05 09:17
题目描述
从
输入:一行两个自然数
输出:所有组合,每个数占三个字符的位置(固定两个空格 + 十进制)。
样例输入 5 3 对应的输出从 1 2 3 开始,共 3 4 5。
思路
组合就是严格递增序列:递归逐位选数,第 %3d 右对齐会在两位数处出错。
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-30 04:53
# update_at: 2026-09-30 04:53
import sys
def combos(n: int, r: int):
"""按字典序递归产出 1..n 中取 r 个的严格递增组合。"""
if r == 0: # 不再取数:当前前缀就是一个完整组合
yield ()
return
# 还要取 r 个数,下一个数最大只能到 n-r+1,否则后面取不满
for head in range(1, n - r + 2):
for tail in combos(n - head, r - 1): # 从 head+1..n 里再取 r-1 个(平移成 1..n-head)
yield (head,) + tuple(head + value for value in tail)
def solve() -> None:
n, r = map(int, sys.stdin.buffer.read().split())
# 题面的「占三个字符」实为标准程序的 " "+十进制:两位数多占一格
lines = (''.join(f' {value}' for value in combo) for combo in combos(n, r))
sys.stdout.write('\n'.join(lines) + '\n')
if __name__ == '__main__':
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-05 09:17
* update_at: 2026-10-05 09:17
*/
// 组合的输出:从 1..n 中按字典序输出所有 r 元组合(递归实现)。
#include <cstdio>
typedef long long ll;
const int MAXR = 25;
ll n, r;
int choose[MAXR]; // choose[i] 表示组合中第 i 个数(严格递增)
// 这一层在选组合的第 dep 个数:从上一个数+1 枚举到 n,保证严格递增
// 递增性天然保证不重不漏,且首元从小到大枚举即得字典序
void dfs(ll dep, ll last) {
if (dep > r) { // r 个数已选满,输出当前组合
for (ll i = 1; i <= r; i++) {
printf(" %d", choose[i]); // 固定两个空格 + 十进制:两位数自然占 3 列
}
printf("\n");
return;
}
// 剪枝:还要取 r-dep+1 个数,最后一个数最大到 n-(r-dep+1)+1
for (ll v = last + 1; v <= n - (r - dep + 1) + 1; v++) {
choose[dep] = v;
dfs(dep + 1, v);
}
}
int main() {
scanf("%lld %lld", &n, &r);
dfs(1, 0); // 从「上一个数是 0」开始,即第一个数从 1 枚举
return 0;
}