题目描述
从 1−n 这 n 个整数中随机选取任意多个,每种方案里的数从小到大排列,按字典序输出所有可能的选择方案。
输入
输入一个整数 n。(1≤n≤10)
输出
每行一组方案,每组方案中两个数之间用空格分隔。
注意每行最后一个数后没有空格。
样例输入
3
样例输出
1
1 2
1 2 3
1 3
2
2 3
3
数据规模与约定
时间限制:1 s
内存限制:256 M
100% 的数据保证 1≤n≤10
解题
为方便分析本问题,我们不妨设定一个数组。该数组长度不小于n,用于存储枚举过程中【一行(即一种选择方案中)】输出的内容。题中保证数据n~[1,10],不妨设置数组arr[11]。
int arr[11];
函数设计
我们不妨分析一下该问题,可见本题枚举出的数据需要满足以下条件:
(1)按字典序输出所有可能的选择方案。
(2)每种方案里的数从小到大排列。
(3)显而易见的,所枚举出的最大的数不能超过n。
为满足上述条件,我们不妨定义变量i,j来控制枚举。
为满足【(1)按字典序输出所有可能的选择方案。】我们定义了变量 i 表示此时我们对于数组中第 i 位进行讨论。在函数设计中使得 i 为递增,则实现按字典序输出;
为满足【(2)每种方案里的数从小到大排列。】我们定义了变量 j ,表示数组中此位置的值最小为 j (即其值必须大于等于 j );在制定一种选择方案中,j随着 i (即数组中位置)的变大而变大,从而实现方案中的数从小到大排列;
条件【(3)显而易见的,所枚举出的最大的数不能超过n。】相当适合作为我们递归函数的边界条件。即:
if(j>=n)return;
于是我们设置递归函数 f 。显然的,它需要 i , j , n 传入三个值。即f(i, j,n)。
我们不妨通过画图来理解该函数:
上图中展示了一个n=6的情况下,此时i = 0表示现在我们关注第0个位置的数,j = 1表示这个位置的数必须大于等于j。而f(0,1,n)则表示用来枚举位置由0到n-1的部分的全部解决方案的集合,即本题需要做到的事情。
而这一张图展示了n=6的情况下,且i=0的位置上的值已知为1的情况。此时i=1的位置上的数必须大于等于2,那么j则赋值为2。函数f(1,2,6)表示图中0位置值固定为1,红框中的值变化,所枚举出的解决方案集合。
如果此时,您能够理解函数f(i,j,n)的含义,那不难发现该题进行输出时应该如下面所示:
最后的f(i+1,n+1,n)显然n+1>n,并不执行,进行return。
下面我们便能够通过此关系设计递归函数:
void f(int i, int j, int n) {
if (j > n)return;//设置边界条件
for (int k = j; k <= n; k++) {
arr[i] = k;
print_result(i);
f(i + 1, k + 1, n);
}return;
}
此处,我们定义函数print—result(i)来输出选择方案。
void print_result(int n) {
for (int i = 0; i <= n; i++) {
if (i)cout << " ";
cout << arr[i] ;
}
cout << endl;
}
最后,我们补上主函数,得到完整代码如下:
#include<iostream>
#include<vector>
#include<string>
using namespace std;
int arr[11];
void print_result(int n) {
for (int i = 0; i <= n; i++) {
if (i)cout << " ";
cout << arr[i] ;
}
cout << endl;
}
void f(int i, int j, int n) {
if (j > n)return;//设置边界条件
for (int k = j; k <= n; k++) {
arr[i] = k;
print_result(i);
f(i + 1, k + 1, n);
}return;
}
int main() {
int n;
cin >> n;
f(0,1,n);
return 0;
}