搜索入门练习题2 全排列 题解

Posted zifeiynoip

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了搜索入门练习题2 全排列 题解相关的知识,希望对你有一定的参考价值。

题目出处:课程=>搜索1=>题目A

题目描述

给定一个正整数 \(n\) ,按照递增顺序打印数字 \(1\)\(n\) 的所有排列。

输入格式

一个整数 \(n(1 \le n \le 7)\)

输出格式

按照递增顺序输出 \(n\) 个数的所有排列,每行代表一组排列, \(n\) 个数两两之间有一个空格分隔。

样例输入

3

样例输出

1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

问题分析

这是一道搜索的题目。
我们知道搜索就是状态到状态之间的转换,其本质是使用递归的方式进行了枚举(注:这句话是非官方解释,不过很便于理解,所以大家先期就这么理解就好)。
这道题目可以用枚举做,但是用枚举做会编写大量的重复代码,所以我们这边使用深度优先搜索来解决。
我们可以开一个 ans[] 数组, ans[id] 用于表示我的当前组合的第 i 个数,然后我开一个函数 void f(int id) 用于表示我当前正准备在组合的第 id 个位置放一个数,然后我只需要从 1 到 n 去遍历一个数 i ,看看 i 能不能放在第 id 个位置(即能否将 ans[id] 设为 i)。
在第 id 个位置能放 i ,当且仅当:ans[1]ans[id-1] 中的元素都不为 i,即 i 还没有放过。
这样,当我们的 f(id) 遍历到 id>n 时,就证明找到了一种排列,输出即可。
实现代码如下:

#include<bits/stdc++.h>
using namespace std;
int n, ans[8];  // ans[i]表示当前排列的第i个数是啥
void f(int id)     // 用于在ans[]数组的第id个位置放数
    if (id > n)    // 边界条件,说明n个数的一个排列找到了
        for (int i = 1; i <= n; i ++)
            cout << (i > 1 ? " " : "") << ans[i];
        cout << endl;
        return; // 输出后返回,不需要继续进行判断了
    
    for (int i = 1; i <= n; i ++)  // 尝试在ans[id]放i
        bool flag = true;
        for (int j = 1; j < id; j ++)
            if (ans[j] == i)   // 说明ans[]数据的第j个位置已经放过i了
                flag = false;
                break;
            
        if (flag)  // flag为true说明i可以放
            ans[id] = i;
            f(id+1);    // 递归地放下一个位置
        
    

int main() 
    cin >> n;
    f(1);
    return 0;

补充知识

这里我会在讲解另外一个实现全排列的方案,但是这种方法并不是使用搜索来实现的,而是每次将当前的这个排列转换成它的下一个排列。比如:1 2 3 4 转换一次会变成 1 2 4 3 ,再转换一次会变成 1 3 2 4,如是循环……
大家可以手动来实现这个程序的编写,但是我们这里先使用 algorithm 库提供给我们的现成的函数——next_permutation
比如,给我们一个数组 a[5] = 1, 2, 3, 4, 5,我们只需要执行一遍 next_permutation(a, a+5),这个数组 a[] 当中的值就会变成它的下一个全排列 1, 2, 3, 5, 4
并且,next_permutation 的返回值是 bool 类型的,如果当前的排列有下一个排列,调用它会返回 true ,同时将当前排列转成下一个排列,如果当前排列已经是全排列里面的最后一个排列了(例如当 a[5]=5, 4, 3, 2, 1就已经是全排列里面的最后一个排列了),它会返回 false
使用 next_permutation 函数解决全排列问题的代码如下:

#include<bits/stdc++.h>
using namespace std;
int n, a[] =  1, 2, 3, 4, 5, 6, 7 ;
void output() 
    for (int i = 0; i < n; i ++)
        cout << (i ? " " : "") << a[i];
    cout << endl;

int main() 
    cin >> n;
    do output(); while (next_permutation(a, a+n));
    return 0;

思考一下:为什么我的代码里面使用了 do...while 循环,而不是 while 循环。

以上是关于搜索入门练习题2 全排列 题解的主要内容,如果未能解决你的问题,请参考以下文章

搜索入门练习题7 最高效益和 题解

搜索入门练习题1 素数环 题解

搜索入门练习题4 数的拆分 题解

搜索入门练习题6 马的遍历 题解

搜索入门练习题5 八皇后问题 题解

搜索练习