问题引出
全排列问题
题目描述
按照字典序输出自然数 1 1 1 到 n n n 所有不重复的排列,即 n n n 的全排列,要求所产生的任一数字序列中不允许出现重复的数字。
输入格式
一个整数 n n n。
输出格式
由 1 ∼ n 1 \sim n 1∼n 组成的所有不重复的数字序列,每行一个序列。
每个数字保留 5 5 5 个场宽。
样例 #1
样例输入 #1
3
样例输出 #1
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
提示
1 ≤ n ≤ 9 1 \leq n \leq 9 1≤n≤9。
方法一:DFS深度优先搜索+剪枝
我们以N=3为例,构造一棵搜索树(或说是状态树)来进行搜索。
同时构造出三个格子,用来存放搜索树中的结果。
现在,我们从第一格开始搜索。第一格填1的搜索树如下:
所以N=3的情况下,第一格填1的排列情况共有两种123,132.
第一格填2的搜索树如下:
所以N=3的情况下,第一格填2的排列情况共有两种 213,231
如果你看懂了,请你自己画画第一格填3的搜索树
我们总结一下在上一部分中的思路在程序中如何实现:
先定义两个数组,一个是用来存放解的,一个是用来标记该数是否用过。
我们可以先写一个用于打印的函数print(),每当深搜时找到一个符合条件的解时,则print()一下,输出这个解(注意题目输出要求)。
接下来就是写深搜的函数了。主要思路:先判断格子是否填满了,如果填满,则print()一下。
如果没有填满,则开始循环,在循环中先判断当前填的数是否用过,如果没有,则填入,搜索下一格。
程序实现如下:
#include <iostream>
#include <iomanip>
using namespace std;
int pd[11]={0};//pd数组用于记录某个数字是否被使用过
int used[11];//used数组用于记录一个序列,如1,3,2或者3,1,2等等
int n;
//提前定义一个print函数方便待会进行输出
void print()
{
for (int i = 1; i <= n; i++)
{
cout<<setw(5)<<used[i];
}
cout<<endl;
}
//定义深度优先函数进行遍历搜索
void dfs(int k)
{
if(k==n)
{
print();
return;
}
for (int i = 1; i <= n; i++)
{
if (!pd[i])//如果当前数没有用过
{
pd[i]=1;//标记一下
used[k+1]=i;//把这个数填入数组
dfs(k+1);//填下一个
pd[i]=0;//回溯
}
}
}
int main()
{
cin>>n;
dfs(0);
}
为了方便理解,我画出了第一格填1和第一格填2时函数的运行过程图:
方法二:利用 C + + < a l g o r i t h m > C++ <algorithm> C++<algorithm>库自带函数
在
C
+
+
a
l
g
o
r
i
t
h
m
C++ algorithm
C++algorithm库当中里有一个函数,全排列函数:
next_permutation!
我管他叫字典排函数;
这个函数每运行一次就可以把数组排成下一个字典排数列;与之对应的是prev_permutation,即排出上一个字典序;
a
l
g
o
r
i
t
h
m
algorithm
algorithm里有好多实用的函数,建议大家百度一下;
回到题上,我们怎样求出所有的组合呢?这是个组合排列问题,形如下图:
那么现在我们做的就是从n个数种拿n个数排列;也就是有n!种排列;理论完毕,代码如下:
#include <algorithm>
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
int n,num=1,a[12];
cin>>n;
for (int i = 1; i <= n; i++)
{
num*=i;
a[i]=n-i+1;//为了使用next_permutation函数,我们得倒着放
}
for (int i = 1; i <= num; i++)
{
next_permutation(a+1,a+n+1);
for (int i = 1; i <= n; i++)
{
cout<<setw(5)<<a[i];
}
cout<<endl;
}
}