题解:洛谷 AT_abc425_b Find Permutation 2

【题目来源】

洛谷:AT_abc425_b [ABC425B] Find Permutation 2 - 洛谷

【题目描述】

给定一个长度为 \(N\) 的整数序列 \(A=(A_1,A_2,\ldots,A_N)\)。其中,\(A\) 的每个元素要么是 \(-1\),要么是 \(1\)\(N\)(包括 \(1\)\(N\))之间的整数。

判断是否存在一个长度为 \(N\) 的整数序列 \(P=(P_1,P_2,\ldots,P_N)\),满足以下所有条件,并在存在时给出任意一个 \(P\)

  • \(P\)\(1\)\(N\) 的一个排列。
  • 对于所有 \(i=1,2,\ldots,N\),如果 \(A_i\neq -1\),则 \(P_i=A_i\)

【输入】

输入从标准输入读取,格式如下:

\(N\) \(A_1\) \(A_2\) \(\ldots\) \(A_N\)

【输出】

如果不存在满足条件的 \(P\),输出 No

否则,输出两行。第一行 Yes,第二行 \(P_1~P_2~\ldots~P_N\),其中 \(P\) 满足所有条件。

如果有多个满足条件的 \(P\),输出其中任意一个。

【输入样例】

4
-1 -1 2 -1

【输出样例】

Yes
3 1 2 4

【核心思想】

  1. 问题分析:给定长度为 \(N\) 的序列 \(A\),其中部分位置为固定值(非 \(-1\)),部分位置自由(\(-1\))。需要判断是否存在一个 \(1\)\(N\) 的排列 \(P\),使得所有非 \(-1\) 位置满足 \(P_i = A_i\)。这是一个排列枚举/约束满足问题,关键在于利用 next_permutation 枚举全排列并检验约束条件。

  2. 算法选择

    • 全排列枚举std::next_permutation 按字典序生成 \(1\)\(N\) 的所有排列,每次 \(O(N)\) 生成下一个
    • 约束检验:对每个生成的排列,检查所有 \(A_i \neq -1\) 的位置是否满足 \(P_i = A_i\)
  3. 关键步骤

    • 读取输入:读入 \(N\) 和序列 \(A[1..N]\)
    • 初始化排列\(p[i] = i\)\(i\)\(1\)\(N\)),作为起始排列
    • 枚举排列do-while 循环配合 next_permutation):
      • 约束检验:遍历 \(i\)\(1\)\(N\),若 \(A_i \neq -1\)\(A_i \neq p[i]\),则当前排列不合法,标记 flag = false 并跳出
      • flag = true:输出 Yes 和该排列,结束程序
    • 若枚举完所有排列仍未找到:输出 No
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N! \cdot N)\),最多枚举 \(N!\) 个排列,每个检验 \(O(N)\)
    • 空间复杂度:\(O(N)\),存储 \(A\)\(p\) 数组
  5. 全排列枚举的核心思想

    • 暴力搜索解空间:排列问题的解空间大小为 \(N!\),当 \(N \le 15\) 时直接枚举可接受
    • 约束剪枝:在排列生成后检验而非生成中剪枝,代码简洁但效率较低;对于更大 \(N\) 需用回溯 + 提前剪枝优化
    • 字典序枚举next_permutation 保证按字典序生成,首次找到的合法解即为字典序最小解之一
    • 适用于"小规模排列约束满足"类问题,核心是正确表达约束条件并遍历解空间

【算法标签】

入门 #DFS-一维

【代码详解】

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

const int N = 15;  // 定义最大元素数量

int n;              // 输入的元素个数
int a[N];           // 存储输入的目标排列(-1表示任意值)
int p[N];           // 存储当前生成的排列

int main()
{
    // 输入元素个数
    cin >> n;

    // 输入目标排列(-1表示该位置可以是任意值)
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }

    // 初始化排列为1~n的顺序排列
    for (int i = 1; i <= n; i++)
    {
        p[i] = i;
    }

    // 生成所有可能的排列并检查是否匹配目标
    do
    {
        bool flag = true;  // 标记当前排列是否匹配目标

        // 检查当前排列是否满足条件
        for (int i = 1; i <= n; i++)
        {
            // 如果目标位置不是-1且与当前排列不匹配
            if (a[i] != -1 && a[i] != p[i])
            {
                flag = false;
                break;
            }
        }

        // 如果找到匹配的排列
        if (flag)
        {
            cout << "Yes" << endl;  // 输出存在解

            // 输出匹配的排列
            for (int i = 1; i <= n; i++)
            {
                cout << p[i] << " ";
            }
            cout << endl;

            return 0;  // 直接结束程序
        }
    } while (next_permutation(p + 1, p + n + 1));  // 生成下一个排列

    // 如果没有找到匹配的排列
    cout << "No" << endl;

    return 0;
}

【运行结果】

4
-1 -1 2 -1
Yes
1 3 2 4
posted @ 2026-08-27 11:02  团爸讲算法  阅读(8)  评论(0)    收藏  举报