top1编程
← 返回题目
题解

降序优先的全排列

1 条题解

  • 0
    @ 2026-8-7 15:19:56

    P4916 降序优先的全排列(入门)

    解题思路

    第一步,理解题意。 给出n个互不相同的正整数,要把它们的所有全排列都输出出来。输出顺序有讲究:要先输出较大的数字,也就是按照字典序从大到小的顺序输出。例如输入9 7 12,第一个输出的排列是12 9 7,最后一个才是7 9 12。

    第二步,先排序。 题目给的数字可能是乱序的,比如9 7 12。我们先把它们从小到大排序,排序后是7 9 12,然后再用从大到小选数字的方式做深度优先搜索,这样生成的排列就正好是字典序从大到小。

    第三步,深度优先搜索。 用一个数组ans存当前已经选好的数字,用used数组标记某个数字是否已经被选。dfs(dep)表示已经选了dep个数字:如果dep等于n,就输出这一行排列;否则,从下标n-1到0倒着枚举还没被选的数字,这样先选大的,选进来继续递归。

    第四步,回溯。 递归返回后要把used标记清掉,让这个数字可以被别的位置再次使用。因为n最大只有9,最多9!约36万种排列,输出量不大。

    第五步,验证样例。 输入3和9 7 12,排序后是7 9 12,倒序选择会依次输出12 9 7、12 7 9、9 12 7、9 7 12、7 12 9、7 9 12,和样例完全一致。插入排序是自己写的小排序,因为n很小,任何排序方法都可以。

    参考代码

    // 降序优先的全排列:先升序排序,再从大到小选数构造排列
    #include <iostream>
    using namespace std;
    int a[12];
    int ans[12];
    int used[12];
    int n;
    void dfs(int dep) {
        if (dep == n) {
            for (int i = 0; i < n; i++) {
                if (i) cout << ' ';
                cout << ans[i];
            }
            cout << endl;
            return;
        }
        for (int i = n - 1; i >= 0; i--) { // 下标从大到小,先选大的数字
            if (used[i]) continue;
            used[i] = 1;
            ans[dep] = a[i];
            dfs(dep + 1);
            used[i] = 0;
        }
    }
    int main() {
        cin >> n;
        for (int i = 0; i < n; i++) cin >> a[i];
        // 插入排序:把a按从小到大排好
        for (int i = 1; i < n; i++) {
            int cur = a[i];
            int j = i - 1;
            while (j >= 0 && a[j] > cur) { a[j + 1] = a[j]; j--; }
            a[j + 1] = cur;
        }
        dfs(0);
        return 0;
    }
    

    复杂度分析

    全排列一共有n!个,每个排列要输出n个数,所以总时间复杂度是O(n·n!)。n最大9,9!约36万,输出总量不大。空间上用了几个长度为n+1的数组,空间复杂度O(n)。由于n很小,这个搜索运行非常快。

    • 1