搜索入门练习题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 循环。

posted @ 2019-09-03 00:46  zifeiynoip  阅读(172)  评论(0编辑  收藏  举报