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

原文地址:https://www.cnblogs.com/zifeiynoip/p/11450698.html

时间: 2024-07-30 16:36:00

搜索入门练习题2 全排列 题解的相关文章

搜索入门练习题3 全组合 题解

题目出处:<信息学奥赛一本通>例5.2. 题目描述 设有 \(n\) 个数的集合 \(\{1,2,...,n\}\) ,从中任意取出 \(r\) 个数进行排列 \((r \le n)\) ,试列出所有的排列. 输入格式 输入包含两个正数 \(n,r(1 \le r \le n \le 10)\) 输出格式 输出从 \(n\) 个数的集合中选出 \(r\) 个数的所有组合,每个组合方案占一行.对于每个组合,按照从小到大的顺序输出组合中的所有元素,两两之间有一个空格分隔. 样例输入 3 2 样例输

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

题目出处:<信息学奥赛一本通>例5.6 题目描述 设有A.B.C.D.E五人从事J1.J2.J3.J4.J5五项工作,每人只能从事一项,他们的效益如下所示. ? J1 J2 J3 J4 J5 A 13 11 10 4 7 B 13 10 10 8 5 C 5 9 7 7 4 D 15 12 10 11 5 E 10 11 8 8 4 每人选择五项工作中的一项,在各种选择的组合中,找到效益最高的一组输出. 题目分析 这道题目其实就是"全排列"问题的变形题,我们可以使用深度优先

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

题目描述 任何一个大于 \(1\) 的自然数 \(n\) ,总可以拆分成若干个小于 \(n\) 的自然数之和.当 \(n = 4\) 时,总共有 \(4\) 种拆分方法: \(4=1+1+1+1\) \(4=1+1+2\) \(4=1+3\) \(4=2+2\) 现在给你一个数 \(n(1 \le n \le 20)\) ,请按顺序输出 \(n\) 的所有拆分方案. 输入格式 输入包含一个整数 \(n(1 \le n \le 20)\) . 输出格式 输出 \(n\) 的所有拆分方案,每种方案占

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

题目出处:<信息学奥赛一本通>例5.1. 题目描述 素数环:从 \(1\) 到 \(n(2 \le n \le 20)\) 这 \(n\) 个数摆成一个环,要求相邻的两个数的和是一个素数. 输入格式 输入包含一个整数 \(n(2 \le n \le 20)\) . 输出格式 按字典序从小到大的顺序输出所有排列方案,每个排列方案占一行.每行的 \(n\) 个数之间由一个空格分隔. 样例输入 2 样例输出 1 2 2 1 问题分析 很明显,这是一道可以用搜索解决的问题,我们可以采用"回溯

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

题目来源:<信息学奥赛一本通>例5.4 题目描述 要在国际象棋棋盘(\(8 \times 8\) 的棋盘)中放 \(8\) 个皇后,使任意两个皇后都不能互相吃.(提示:皇后能吃同一行.同一列.同一对角线的任意棋子.) 输出格式 输出一个整数,用于表示八皇后问题的放置方案. 题目分析 首先我们用 \((x,y)\) 来表示棋盘上第 \(x\) 行第 \(y\) 列的格子的坐标. 那么,两个皇后 \((x_1,y_1)\) 和 \((x_2,y_2)\) 会互相攻击当且仅当满足如下条件之一: 在同

搜索入门之dfs--经典的迷宫问题解析

今天来谈一下dfs的入门,以前看到的dfs入门,那真的是入门吗,都是把dfs的实现步骤往那一贴,看完是知道dfs的步骤了,但是对于代码实现还是没有概念.今天准备写点自己的心得,真的是字面意思--入门. DFS,即深度优先搜索,是一种每次搜索都向尽可能深的地方去搜索,到达尽头时再回溯进行其他结点去搜索的搜索策略.形象的说,这是一种“不撞南墙不回头”的策略. 其实也就是遍历,只不过不像一个线性数组的遍历那么直观罢了.说到回溯,每次看到这个词我都得思考一会,到底是怎么用栈进行回溯的呢?今天实际操作了一

POJ 1579 Function Run Fun 【记忆化搜索入门】

题目传送门:http://poj.org/problem?id=1579 Function Run Fun Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 20560   Accepted: 10325 Description We all love recursion! Don't we? Consider a three-parameter recursive function w(a, b, c): if a <=

HDU ACM 4734 F(x)-&gt;数位DP入门练习题

分析:数位DP的入门联系题,通过该題对数位DP有感觉了:dp[len][presum]代表长度为len权值不大于presum的数.记忆化搜索提高效率,除边界(因为边界上算出来的答案在其它数中可能是不完整的)外记录dp[len][presum]的值,下次发现以前计算过就直接return:dfs(len, presum, fg)表示求长度为len,不超过pre的全部符合条件的值.fg是控制边界的. #include<iostream> using namespace std; int dp[11]

深度搜索入门

深度优先搜索是搜索的手段之一.它从某个状态开始,不断地转移状态,直到无法转移,然后回退到前一步的状态,继续转移到其他状态,如此重复,直到找到最终的解. 做这类题目,抓住两样东西:1.总体上递归几次(几层)?每一次递归确定一层上的数. 2.每次递归,有几种选择的情况.所以dfs()函数,只有两部分(if.else结构):1.(if部分)若每一层都选择了,判断是否符合条件,做出题目要求的操作.2.(else部分)若还有层没有选择,就做出选择,所有选择的情况列出. 下面是几个考察dfs的题目: 1.部