题解:洛谷 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
【核心思想】
-
问题分析:给定长度为 \(N\) 的序列 \(A\),其中部分位置为固定值(非 \(-1\)),部分位置自由(\(-1\))。需要判断是否存在一个 \(1\) 到 \(N\) 的排列 \(P\),使得所有非 \(-1\) 位置满足 \(P_i = A_i\)。这是一个排列枚举/约束满足问题,关键在于利用
next_permutation枚举全排列并检验约束条件。 -
算法选择:
- 全排列枚举:
std::next_permutation按字典序生成 \(1\) 到 \(N\) 的所有排列,每次 \(O(N)\) 生成下一个 - 约束检验:对每个生成的排列,检查所有 \(A_i \neq -1\) 的位置是否满足 \(P_i = A_i\)
- 全排列枚举:
-
关键步骤:
- 读取输入:读入 \(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和该排列,结束程序
- 约束检验:遍历 \(i\) 从 \(1\) 到 \(N\),若 \(A_i \neq -1\) 且 \(A_i \neq p[i]\),则当前排列不合法,标记
- 若枚举完所有排列仍未找到:输出
No
-
时间/空间复杂度:
- 时间复杂度:\(O(N! \cdot N)\),最多枚举 \(N!\) 个排列,每个检验 \(O(N)\)
- 空间复杂度:\(O(N)\),存储 \(A\) 和 \(p\) 数组
-
全排列枚举的核心思想:
- 暴力搜索解空间:排列问题的解空间大小为 \(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
浙公网安备 33010602011771号