题解:洛谷 P1706 全排列问题

【题目来源】

洛谷:P1706 全排列问题 - 洛谷(luogu.com.cn)

【题目描述】

按照字典序输出自然数 \(1\)\(n\) 所有不重复的排列,即 \(n\) 的全排列,要求所产生的任一数字序列中不允许出现重复的数字。

【输入】

一个整数 \(n\)

【输出】

\(1∼n\) 组成的所有不重复的数字序列,每行一个序列。

每个数字保留 \(5\) 个场宽。

【输入样例】

3

【输出样例】

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

【核心思想】

  1. 问题分析:给定 \(n\),要求输出 \(1\)\(n\) 的所有全排列,按字典序输出,每个数字占 5 个场宽。这是经典的全排列生成问题,可以使用深度优先搜索(DFS)枚举所有排列,通过标记数组记录已使用的数字,避免重复。

  2. 算法选择

    • DFS + 回溯:递归函数 dfs(step) 表示当前正在填充第 step 个位置(从 0 或 1 开始)。若 step == n,则输出当前排列。否则,循环尝试将每个未使用的数字放入当前位置,标记后递归,回溯时取消标记。
    • 输出格式:使用 setw(5) 设置每个数字宽度为 5。
  3. 关键步骤

    • 读入:读取 \(n\)
    • DFSdfs(step)
      • step == n,输出 a[0..n-1](或 a[1..n]),返回。
      • 循环 i 从 1 到 \(n\)
        • book[i] == 0
          • a[step] = ibook[i] = 1
          • 递归 dfs(step+1)
          • book[i] = 0(回溯)。
    • 输出:使用 cout << setw(5) << a[i]
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n! \times n)\)\(n\) 通常较小(\(n \le 9\)),完全可行。
    • 空间复杂度:\(O(n)\)
  5. DFS 与全排列

    • 回溯思想:通过标记数组记录已用数字,递归填下一个位置,递归返回后撤销标记,尝试其他数字。
    • 字典序:由于循环从小到大尝试数字,生成的排列自然按字典序排列。
    • 适用场景:全排列生成是 DFS 最经典的应用之一。

【解题思路】

【算法标签】

普及- #DFS-一维

【代码详解】

#include <bits/stdc++.h>
using namespace std;

int n, a[10], book[10] = {0};  // 定义全局变量:n-排列长度,a-存储当前排列,book-标记数组

// 深度优先搜索生成全排列
void dfs(int step)
{
    // step: 当前正在填充的位置索引(从0开始)

    // 终止条件:已填满整个排列
    if (step == n)
	{
        // 输出当前排列(每个数字占5位宽度)
        for (int i = 0; i < n; i++)
            cout << setw(5) << a[i];
        cout << endl;
        return;
    }

    // 尝试所有可能的数字(1到n)
    for (int i = 1; i <= n; i++)
	{
        // 如果数字i未被使用
        if (book[i] == 0)
		{
            a[step] = i;    // 将数字i放入当前位置
            book[i] = 1;    // 标记数字i为已使用
            dfs(step + 1);  // 递归处理下一个位置
            book[i] = 0;    // 回溯,取消数字i的标记
        }
    }
}

int main()
{
    cin >> n;  // 输入排列长度n
    dfs(0);    // 从第0个位置开始生成排列
    return 0;
}
#include <bits/stdc++.h>
using namespace std;

// 全局变量声明
int n;          // 排列长度(1到n的全排列)
int a[10];      // 存储当前生成的排列(索引1到n)
int book[10] = {0}; // 标记数组,记录数字是否已使用(0未使用,1已使用)

// 深度优先搜索函数
void dfs(int step)
{
    // step表示当前正在处理的位置(从1开始)

    // 终止条件:当step超过n时,说明已经生成了一个完整的排列
    if (step == n + 1)
	{
        // 输出当前排列(每个数字占5位宽度)
        for (int i = 1; i <= n; i++)
            cout << setw(5) << a[i];  // 格式化输出
        cout << endl;  // 换行
        return;        // 返回上一层递归
    }

    // 尝试将每个数字放入当前位置
    for (int i = 1; i <= n; i++)
	{
        // 检查数字i是否未被使用
        if (book[i] == 0)
		{
            a[step] = i;    // 将数字i放入当前位置
            book[i] = 1;    // 标记数字i为已使用
            dfs(step + 1);  // 递归处理下一个位置
            // 回溯操作
            a[step] = 0;    // 清除当前位置的值(可省略)
            book[i] = 0;    // 取消数字i的标记
        }
    }
}

int main()
{
    cin >> n;    // 输入排列长度n
    dfs(1);      // 从第1个位置开始生成排列
    return 0;
}

【运行结果】

3
    1    2    3
    1    3    2
    2    1    3
    2    3    1
    3    1    2
    3    2    1
posted @ 2026-08-31 09:25  团爸讲算法  阅读(18)  评论(0)    收藏  举报