AtCoder真题及详细题解 ABC425B: Find Permutation 2
AtCoder真题及详细题解 ABC425B: Find Permutation 2
题目描述
给定一个长度为 NNN 的整数序列 A=(A1,A2,…,AN)A=(A_1,A_2,\ldots,A_N)A=(A1,A2,…,AN)。其中,AAA 的每个元素要么是 −1-1−1,要么是 111 到 NNN(包括 111 和 NNN)之间的整数。
判断是否存在一个长度为 NNN 的整数序列 P=(P1,P2,…,PN)P=(P_1,P_2,\ldots,P_N)P=(P1,P2,…,PN),满足以下所有条件,并在存在时给出任意一个 PPP。
- PPP 是 111 到 NNN 的一个排列。
- 对于所有 i=1,2,…,Ni=1,2,\ldots,Ni=1,2,…,N,如果 Ai≠−1A_i\neq -1Ai=−1,则 Pi=AiP_i=A_iPi=Ai。
输入格式
输入从标准输入读取,格式如下:
NNN A1A_1A1 A2A_2A2 …\ldots… ANA_NAN
输出格式
如果不存在满足条件的 PPP,输出 No。
否则,输出 Yes P1P2…PNP_1~P_2~\ldots~P_NP1 P2 … PN,其中 PPP 满足所有条件。
如果有多个满足条件的 PPP,输出其中任意一个。
输入输出样例 1
输入 1
4
-1 -1 2 -1
输出 1
Yes
3 1 2 4
输入输出样例 #2
输入 2
5
-1 -1 1 -1 1
输出 #2
No
输入输出样例 3
输入 3
7
3 -1 4 -1 5 -1 2
输出 3
Yes
3 7 4 1 5 6 2
说明/提示
样例解释 1
P=(3,1,2,4)P=(3,1,2,4)P=(3,1,2,4) 满足所有条件。
除此之外,P=(1,3,2,4)P=(1,3,2,4)P=(1,3,2,4) 或 P=(4,3,2,1)P=(4,3,2,1)P=(4,3,2,1) 也满足所有条件。
样例解释 2
不存在满足所有条件的 PPP。
数据范围
- 1≤N≤101\le N\le 101≤N≤10
- Ai=−1A_i=-1Ai=−1 或 1≤Ai≤N1\le A_i \le N1≤Ai≤N
- 所有输入值均为整数。
AC代码
#include <bits/stdc++.h>
using namespace std;int n;
int a[20], p[20]; // a存储输入序列,p存储当前尝试的排列
bool used[20]; // 标记数字是否已被使用// 深度优先搜索函数,尝试构建排列
// pos: 当前正在处理的位置
bool dfs(int pos) {// 如果已经处理完所有位置,返回成功if (pos == n + 1) return true;// 如果当前位置有固定值if (a[pos] != -1) {int val = a[pos];// 如果该值未被使用if (!used[val]) {p[pos] = val; // 将固定值放入排列used[val] = true; // 标记该值已使用// 递归处理下一个位置if (dfs(pos + 1)) return true;used[val] = false; // 回溯:取消标记}return false; // 如果固定值已被使用,返回失败} // 如果当前位置没有固定值,尝试所有可能的值else {for (int val = 1; val <= n; val++) {// 如果该值未被使用if (!used[val]) {p[pos] = val; // 尝试将该值放入排列used[val] = true; // 标记该值已使用// 递归处理下一个位置if (dfs(pos + 1)) return true;used[val] = false; // 回溯:取消标记}}return false; // 所有可能值都尝试过但都不成功,返回失败}
}int main() {cin >> n;// 读取输入并初步检查重复for (int i = 1; i <= n; i++) {cin >> a[i];if (a[i] != -1) {// 如果固定值重复出现,直接输出"No"if (used[a[i]]) {cout << "No" << endl;return 0;}used[a[i]] = true; // 标记固定值已使用}}// 重置used数组,准备深度优先搜索// 注意:这里重置了数组,但前面已经检查过固定值重复,所以是安全的memset(used, false, sizeof(used));// 使用深度优先搜索尝试构建排列if (dfs(1)) {// 如果找到解,输出结果cout << "Yes" << endl;for (int i = 1; i <= n; i++) {cout << p[i] << " ";}cout << endl;} else {// 如果找不到解,输出"No"cout << "No" << endl;}return 0;
}
功能分析
核心算法
- 深度优先搜索(DFS): 使用回溯法尝试构建满足条件的排列
- 回溯法: 当某条路径无法继续时,撤销当前选择,尝试其他可能性
主要步骤
-
输入处理与初步检查
- 读取序列长度n和序列A
- 检查固定值是否有重复,如有重复直接输出"No"
-
DFS构建排列
- 从位置1开始递归处理每个位置
- 对于有固定值的位置:直接使用该值(如果可用)
- 对于无固定值的位置:尝试所有未使用的数字
- 使用used数组记录哪些数字已被使用
-
输出结果
- 找到解:输出"Yes"和排列P
- 无解:输出"No"
时间复杂度
- 最坏情况:O(n!),但由于n≤10,在可接受范围内
- 实际运行:通过剪枝和提前终止,通常远好于最坏情况
文末福利:《csp信奥赛12大高频考点专题集训》
https://edu.csdn.net/course/detail/40437 点击跳转到秘籍

更多csp信奥赛c++完整系列课程,点击查看老师的个人主页:
https://edu.csdn.net/lecturer/7901 点击链接

#include<bits/stdc++.h>
using namespace std;
int main(){cout<<"############# 祝看到这篇秘籍的OIer: ###############";cout<<"#### AC不是偶然,一等奖是必然!祝你AK全场! ########";cout<<"############# 复赛夺魁,一等在手! #################";return 0;
}
