题目描述
小蓝在环球旅行时来到了一座古代遗迹,里面并排放置了 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 的一个排列。
题目的几个重点
- a 是 1 至 n 的一个排列
若将传送门和他的目的地连接在一起成为一个有向图,可以发现整张图构成了数个环。
- 每次使用魔法只能进入相邻的传送阵
只有环内数据相邻的环才能在环间使用魔法。
- 一个传送阵可多次进入
只要满足上一条的条件,即可在环间使用魔法而无需在意遍历顺序。
让我们仔细分析一下
根据上述的条件,不难得出:
- 在环数≤2时,可以轻松地遍历所有传送门,即经过传送门数最大为传送门总数量n。
- 在环数>2时,需要找到相邻的环,这两个相邻的环中所含传送门的数目最大。
我们需要写什么?
- 储存环的数量和环内所含元素的容器
- 检测两环是否相邻的函数
- 遍历整个环的函数
由此,我们将整道题分割完毕。
完整代码
#include<iostream>
#include<vector>
#include <algorithm>
using namespace std;
bool connected(vector<int> ring1, vector<int> ring2)
{
for (int i = 0; i < ring1.size(); i++)
{
if (count(ring2.begin(), ring2.end(), ring1[i] + 1)\
|| count(ring2.begin(), ring2.end(), ring1[i] - 1)) {
return true;
}
}
return false;
}
int getAns(vector<int> a, int n)
{
vector<bool> vis(n + 1, false);
vector<vector<int>> rings;
for (int i = 1; i <= n; i++) {
if (!vis[i]) {
vis[i] = true;
vector<int> thisRing;
thisRing.push_back(i);
int next = a[i];
while (!vis[next]) {
vis[next] = true;
thisRing.push_back(next);
next = a[next];
}
rings.push_back(thisRing);
}
}
int ringSize = rings.size();
if (ringSize <= 2) return n;
int ans = 0;
for (int i = 0; i < rings.size(); i++) {
for (int j = i + 1; j < rings.size(); j++) {
bool connect = connected(rings[i], rings[j]);
if (connect) ans = max(ans, (int)(rings[i].size() + rings[j].size()));
}
}
return ans;
}
int main()
{
int t, n;
cin >> n;
vector<int> a;
a.push_back(0);
for (int i = 0; i < n; i++) {
cin >> t;
a.push_back(t);
}
int ans = getAns(a, n);
cout << ans;
}