蓝桥杯2024年第十五届省赛真题-传送阵
题目描述
小蓝在环球旅行时来到了一座古代遗迹,里面并排放置了 n 个传送阵,进入第 i 个传送阵会被传送到第 ai 个传送阵前,并且可以随时选择退出或者继续进入当前传送阵。小蓝为了探寻传送阵中的宝物,需要选择一个传送阵进入,然后连续进入之后的传送阵。小蓝希望尽可能多地进入传送门以便搜索宝物,同时他可以使用一次魔法,从某个传送阵 j 走到相邻的(第 j − 1 或第 j + 1 个)传送阵,请问小蓝最多能到达多少个不同的传送阵?一个传送阵可多次进入,但在计算答案时只算一个。
输入格式
输入的第一行包含一个正整数 n 。第二行包含 n 个正整数 a1, a2, · · · , an ,相邻整数之间使用一个空格分隔。
输出格式
输出一行包含一个整数表示答案。
样例输入
5
2 1 5 4 3
样例输出
4
提示
【样例说明】
小蓝的路径可以是:1 → 2 → 3 → 5 。其中 2 → 3 使用魔法。
【评测用例规模与约定】
对于 20% 的评测用例,1 ≤ n ≤ 1000 ;对于所有评测用例,1 ≤ n ≤ 106,且 a 是 1 至 n 的一个排列。
思路:这段代码主要使用并查集(Union-Find)数据结构来解决问题。魔法的使用类似与一次合并,而在合并时可以默认将后出现的传送阵合并到先出现的传送阵中,也就先出现的传送阵当父节点,比如案例中的2为1的父节点,5为3的父节点,所以在cnt的存储中会出现2 1 2 1 1的情况,这个cnt存储的数据就是表示以该第i个传送的为父节点的树的结点的个数。而在最后算可以经过传送阵总和的时候,只需要找到该传送阵的父节点,就可以知晓出首先到达这个传送阵后一共可以经过几个传送阵。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1e6 + 10;
int arr[N]; int cnt[N];
int find(int x) {//找到最终父节点,并递归将每个节点的父节点都设置为最终父节点
if (x != arr[x]) {
arr[x] = find(arr[x]);
}
return arr[x];
}
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
arr[i] = i; cnt[i] = 1;
}
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
int a = find(i);int b = find(x);//找到i与x的集合代表元素
if (a == b) {//如果 i 和 x 已经在同一个集合中,则跳过本次合并操作。
continue;
}
cnt[a] += cnt[b];//将集合 b 合并到集合 a 中,更新集合 a 的元素数量,并将 b 的父节点设为 a
arr[b] = a;
}
int count = 0;
for (int i = 1; i <= n; i++) {
int a = find(i); int b = find(i+1);
if (a == b) {//如果 a 和 b 相同,说明它们在同一个集合中,更新 count 为 count 和集合 a 元素数量的最大值。
count = max(count, cnt[a]);
}
else {//否则,说明它们不在同一个集合中,更新 count 为 count 和集合 a 与集合 b 元素数量之和的最大值。
count = max(count, cnt[a] + cnt[b]);
}
}
cout << count;
}