Problem Description
排列与组合是常用的数学方法,其中组合就是从n个元素中抽出r个元素(不分顺序且r < = n),我们可以简单地将n个元素理解为自然数1,2,…,n,从中任取r个数。
现要求你不用递归的方法输出所有组合。
Input
一行两个自然数n、r ( 1 < n < 21,1 < = r < = n )。
Output
所有的组合,每一个组合占一行且其中的元素按由小到大的顺序排列,所有的组合也按字典顺序。
Sample Input
5 3
Sample Output
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
原题链接
解题思路
- 思路:更新最后一位,最后一位更新到n时,更新上一位。
- 声明一个栈,将数字1入栈。
- 取栈顶的下一位入栈。
- 当栈内元素个数为r时,打印栈内元素,需要出栈一次,更新栈顶。
- 当栈顶为n时,需要出栈两次,更新栈顶。
- 重复③④⑤操作,栈为空为止。
示例:
- 栈内元素:1,2,3,此时元素个数等于3,打印,需要一次出栈。
- 3出栈,3的下一位4入栈,栈内元素:1,2,4。
- 此时元素个数等于3,打印,需要一次出栈。
- 4出栈,4的下一位5入栈,栈内元素:1,2,5。
- 由于栈顶为5,需要两次出栈。
- 5出栈,2出栈,2的下一位3入栈,栈内元素:1,3。
经验总结
- 递归思路:
- 用index记录当前处理的整数的编号,count记录当前组合中数字的个数。
- 递归边界:
- 选择第n + 1个数时结束递归,此时若count == r,则需打印组合。
- count == r时结束递归。
- index从1开始,每个index有两种选择:
- 选择当前整数index,count + 1,考虑下一个整数(即index + 1)。
- 不选择当前整数index,考虑下一个整数。
代码实现(C)
- 迭代方案:
#include <stdio.h>
#include <stdbool.h>
#define MaxSize 21
// 打印组合方案
void print(int a[], int count) {
for (int i = 0; i < count; ++i) {
if (i < count - 1)
printf("%d ", a[i]);
else
printf("%d\n", a[i]);
}
}
int main() {
int n, r;
while (~scanf("%d%d", &n, &r)) {
int top = 0; // 栈顶指针top指向栈顶元素后一个位置,初值为0
int stk[MaxSize]; // 声明栈
bool needPop = false; // 记录是否需要出栈
int index = 1; // index记录上一次选择的整数(即栈顶)
stk[top++] = index; // 1入栈
while (top > 0) { // 一直循环到栈空
if (top == r) { // 如果栈内元素个数等于r
print(stk, top); // 打印输出栈内元素
needPop = true; // 需要出栈最后一位元素
}
index = stk[top - 1]; // 取栈顶元素
if (index == n) { // 到达整数边界,需要出栈两次
top--;
needPop = true;
continue;
}
if (needPop) { // 出栈
top--;
needPop = false;
}
if (index < n) // 未到数字边界
stk[top++] = index + 1; // 选择栈顶元素的下一个数字入栈
}
}
return 0;
}
- 递归方案:
#include <stdio.h>
#define MaxSize 21
int n, r; // 从n个数中选出r个数
int ans[MaxSize]; // 记录组合方案
// 打印组合方案
void print(int index) {
for (int i = 0; i < index; ++i) {
if (i < index - 1)
printf("%d ", ans[i]);
else
printf("%d\n", ans[i]);
}
}
void dfs(int index, int count) {
/*递归边界
* ①选择第n+1个数时,若当前组合中数字个数为r则打印并返回
* ②当前组合中数字个数为r则打印并返回
* */
if (index == n + 1 || count == r) {
if (count == r)
print(count);
return;
}
// 选择index
ans[count] = index;
dfs(index + 1, count + 1);
// 不选择index
dfs(index + 1, count);
}
int main() {
while (~scanf("%d%d", &n, &r)) {
// 从1开始选择,初始时组合中的个数为0
dfs(1, 0);
}
return 0;
}