kedaOJP0015. 全排列

题目描述

我们称 n 的全排列为 1,2,3...,1,2,3...,n 按照一定顺序组成的序列。比如 33 的所有全排列为:

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

现在请你生成 n 的所有全排列。

输入格式

第一行一个整数 n。

输出格式

输出所有 n 的全排列,按照字典序从小到大输出。

字典序比较大小方法为从第一个数字开始比较,若不同则比较出来大小,否则接着比较第二位,直到比较出来大小位置。样例中就是按照字典序排序后的结果。

输入数据 1

3

输出数据 1

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

数据范围与提示

1<=n<=81<=n<=8

这个全排列其实很简单,它的生成算法主要有两种:递归方法和字典序生成方法。

1. 递归方法(回溯算法)

递归方法是生成全排列的一种直观方式。它通过逐个确定每个位置上的元素来构造排列。

  • 初始时,所有元素未被选中。
  • 从第一个位置开始,递归地选择一个未被选中的元素放到这个位置上。
  • 然后对剩下的未被选中的元素做同样的操作。
  • 当所有位置都被填满后,一个全排列就生成了。
  • 然后回溯,更换前面位置的元素,继续生成新的排列。

这个过程类似于树的深度优先遍历。

2. 字典序生成方法

字典序方法是另一种高效的生成全排列的方法,它不依赖递归。

  • 首先,将原始序列升序排序。
  • 然后不断找出当前排列的下一个排列,直到不存在更大的排列。
  • 找下一个排列的步骤是:
    1. 从后向前查找第一个相邻升序的元素对 (i, j),满足 A[i] < A[j]。这一步是找到需要改变的分界点。
    2. 再次从后向前查找第一个大于 A[i] 的元素 A[k]
    3. 交换 A[i]A[k]
    4. 反转 A[i] 后面的所有元素。

例如,对于序列 [1, 2, 3],其全排列按字典序为 [1, 2, 3] -> [1, 3, 2] -> [2, 1, 3] -> [2, 3, 1] -> [3, 1, 2] -> [3, 2, 1]

我这里用的是字典序:

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

int main() {
    int n;
    cin >> n;
    vector<int> nums(n);
    
    
    for (int i = 0; i < n; ++i) {
        nums[i] = i + 1;
    }


    do {
        for (int i = 0; i < n; ++i) {
            cout << nums[i] << ' ';
        }
        cout << '\n';
    } while (next_permutation(nums.begin(), nums.end()));

    return 0;
}

代码的工作原理:

  1. 读取输入: 程序首先从标准输入读取一个整数n

  2. 初始化向量: 然后,程序创建一个std::vector<int>,名为nums,其大小为n,并用1到n的连续整数初始化。

  3. 生成排列:

    • 使用do-while循环和std::next_permutation函数,程序生成并打印出nums向量的所有排列。
    • 在每次迭代中,使用std::next_permutation来生成下一个字典序更大的排列。
    • 当所有排列都被生成后,next_permutation将返回false,循环结束。
  4. 打印排列:

    • 在循环的每次迭代中,打印当前的排列,每个数字后跟一个空格。
    • 每个排列后打印一个换行符。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值