题目描述
从 1−n 这 n 个整数排成一排并打乱次序,按字典序输出所有可能的选择方案。
输入
输入一个整数 n。(1≤n≤8)
输出
每行一组方案,每组方案中两个数之间用空格分隔。
注意每行最后一个数后没有空格。
样例输入
3
样例输出
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
数据规模与约定
时间限制:1 s
内存限制:256 M
100% 的数据保证 1≤n≤8
解题分析
本题与前两篇相比,递归函数类似,但是可见在本题中,同一方案中前面的数是可以大于后面的数的(譬如“1 3 2”)。为实现这一点,我们会希望能够通过某种方法得知现在将要枚举的数是否已经被枚举过了。这是解决本题的关键所在。
第一步、照常定义输出函数,输出每一个解决方案
void print_arr(int n) {
for (int i = 0; i < n; i++) {
if (i)cout << " ";
cout << arr[i];
}
cout << endl;
return;
}
第二步、设置数组标记该数字是否被使用过
这一步该如何实现?由题知本题n<=8,我们不妨定义数组vis[10],当数字k未被枚举时,vis[k]=0;当枚举数字k时,同时标记vis[k]=1,从而满足本题要求。
int vis[10]={0};
第三步、设计递归函数
在学会指数型枚举和组合型枚举后,这一步将变得十分熟悉。
首先定义数组arr[10]来存放一个解决方案中的n个数字;
设计f(i,n),其中i表示现在枚举到arr数组的第i位,f(i,n)表示第i位置到第n位置的枚举方案集合。
其中,v[1]代指第1个未被枚举过的数,v[2]代指第2个未被枚举过的数...以此类推
显然边界条件为:当i==n时,输出该方案。
那么我们不难写出该递归函数:
void f(int i,int n) {
if (i == n) {
print_arr(n);
return;
}
for (int k = 1; k <= n; k++) {
if (vis[k])continue;//判断k是否被枚举过
arr[i] = k;
vis[k] = 1;
f(i + 1, n);
vis[k] = 0;//此时已经输出完所有第i位置等于k的结果,我们可以设置vis[k]=0使k可以在后面的枚举继续被使用
}
return;
}
补全程序,获得完整代码如下:
#include <iostream>
using namespace std;
int arr[10], vis[10] = {0};
void print_arr(int n) {
for (int i = 0; i < n; i++) {
if (i)cout << " ";
cout << arr[i];
}
cout << endl;
return;
}
void f(int i,int n) {
if (i == n) {
print_arr(n);
return;
}
for (int k = 1; k <= n; k++) {
if (vis[k])continue;//判断k是否被枚举过
arr[i] = k;
vis[k] = 1;
f(i + 1, n);
vis[k] = 0;//此时已经输出完所有第i位置等于k的结果,我们可以设置vis[k]=0使k可以在后面的枚举继续被使用
}
return;
}
int main()
{
int n;
cin >> n;
f(0, n);
return 0;
}
标签:10,arr,排列,递归,int,枚举,本题
From: https://blog.csdn.net/weixin_74873063/article/details/139120009