题解:洛谷 AT_abc461_b The Honest Woodcutters
【题目来源】
洛谷:AT_abc461_b [ABC461B] The Honest Woodcutters - 洛谷
【题目描述】
有 \(N\) 个樵夫编号为 \(1, 2, \dots, N\),每个人都有一把斧头。现在他们都把自己的斧头掉进了池塘。池塘里有 \(N\) 把斧头编号为 \(1, 2, \dots, N\) 被捞了上来。
每个樵夫 \(i\) 声称“我拥有第 \(A_i\) 号斧头”。而池塘女神则知道第 \(i\) 号斧头的真正主人是编号为 \(B_i\) 的樵夫。
请判断所有 \(N\) 个樵夫是否都讲了真话。
【输入】
输入通过标准输入给出,格式如下:
\(N~A_1~A_2~\dots~A_N~B_1~B_2~\dots~B_N\)
【输出】
如果所有 \(N\) 个樵夫都讲了真话,输出 Yes;否则输出 No。
【输入样例】
3
3 1 2
2 3 1
【输出样例】
Yes
【核心思想】
-
问题分析:给定 \(N\) 个樵夫,每人声称拥有第 \(A_i\) 号斧头,而第 \(i\) 号斧头的真正主人是 \(B_i\)。需要判断所有樵夫是否都说真话,即对于每个樵夫 \(i\),他声称的斧头 \(A_i\) 的真正主人是否恰好是他自己。等价于验证:对于所有 \(i\),第 \(A_i\) 号斧头的真正主人 \(B_{A_i}\) 等于 \(i\)。或者换个角度:建立"斧头 \(\to\) 声称者"的映射和"斧头 \(\to\) 真正主人"的映射,验证两者是否完全一致。
-
算法选择:
- 映射构建与比对:将 \(A\) 数组转化为"斧头 \(x\) 被谁声称"的逆映射,然后与 \(B\) 数组逐位比较
-
关键步骤:
- 读入数据:\(N\),数组 \(A[1..N]\) 和 \(B[1..N]\)
- 构建逆映射:\(pos[A_i] = i\),表示第 \(A_i\) 号斧头被樵夫 \(i\) 声称
- 逐位验证:对于 \(i\) 从 \(1\) 到 \(N\):
- 若 \(pos[i] \neq B_i\),输出
No并结束
- 若 \(pos[i] \neq B_i\),输出
- 输出结果:全部匹配则输出
Yes
-
时间/空间复杂度:
- 时间复杂度:\(O(N)\),构建逆映射 \(O(N)\),验证 \(O(N)\)
- 空间复杂度:\(O(N)\),逆映射数组
-
逆映射与一致性验证的核心思想:
- 双向验证的等价性:樵夫 \(i\) 说真话 \(\Leftrightarrow\) 他声称的斧头 \(A_i\) 的真正主人是他。即 \(B_{A_i} = i\)。代码通过构建逆映射 \(pos[x] = i\)(斧头 \(x\) 被樵夫 \(i\) 声称),然后验证 \(pos[i] = B_i\)(斧头 \(i\) 被樵夫 \(pos[i]\) 声称,而真正主人是 \(B_i\)),两者等价
- 逆映射的巧妙构建:读入 \(A_i\) 时直接记录 \(a[A_i] = i\),将"谁声称哪把斧头"转化为"哪把斧头被谁声称",避免后续查找
- 提前终止优化:一旦发现不匹配立即输出
No并退出,最坏情况才需完整遍历 - 排列性质利用:\(A\) 是 \(1\) 到 \(N\) 的排列(每人声称不同的斧头),保证逆映射无冲突,无需处理重复
- 适用于映射验证、排列比对、双向一致性检查类基础问题
【算法标签】
入门 #模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 105; // 定义最大数量
int n; // 元素数量
int a[N], b[N]; // 位置数组a,比较数组b
int main() // 主函数
{
cin >> n; // 输入元素数量
for (int i=1; i<=n; i++) // 读取第一组数据
{
int x; cin >> x; // 输入数字
a[x] = i; // 记录数字x在数组a中的位置
}
for (int i=1; i<=n; i++) // 读取第二组数据
cin >> b[i]; // 输入第二组数字
for (int i=1; i<=n; i++) // 比较两个数组
if (a[i]!=b[i]) // 如果位置不匹配
{
cout << "No" << endl; // 输出No
return 0; // 结束程序
}
cout << "Yes" << endl; // 所有位置都匹配,输出Yes
return 0; // 程序正常结束
}
【运行结果】
3
3 1 2
2 3 1
Yes
浙公网安备 33010602011771号