返回题解分享
讨论 / 题解分享/ 帖子详情

传送阵(编程题) - 题解

并查集

n个传送阵可能会构成多个环,利用并查集把n个传送阵构造成若干个环,并让每个根节点记录对应环的长度。
最后遍历每个传送阵,判断当前传送阵和前后邻近的传送阵是否连通,不连通则说明可以使用魔法,将两个环的长度加起来;连通则说明在同一个环里。结果取最大值

#include <iostream>
using namespace std;
const int N = 1e6+2;
int n;
int fa[N];

void init()
{
  for(int i = 1;i<=n;i++)
   fa[i] = i;
}

int root(int x)
{
  return fa[x] = (x == fa[x]? x : root(fa[x]));
}

void add(int x ,int y)
{
  if(root(x) != root(y))
  {
    fa[root(x)] = root(y);
  }
}

int main()
{
  cin>>n;
  int next[n+1];
  int num[n+2] = {0};
  bool v[n+1] = {0};
  init();
  for(int i = 1;i<=n;i++)
  {
    cin>>next[i];
  }
  for(int i = 1;i<=n;i++)
  {
    if(v[i]) continue;
    v[i] = true;
    int now = i;
    int cnt = 1;
    while(next[now] != i)
    {
      add(now, next[now]);
      v[next[now]] = true;
      now = next[now];
      cnt++;
    }
    num[root(i)] = cnt;
  }
  int ans = 0;
  for(int i = 1;i<=n;i++)
  {
    if(root(i)!= root(i-1))
     ans = max(ans, num[root(i)] + num[root(i-1)]);
    if(root(i) != root(i+1))
     ans = max(ans, num[root(i)] + num[root(i+1)]);
  }
  cout<<ans;
  return 0;
}
2 回复 0 转发 2 喜欢 9 阅读
回复 (2)
默认 最新
露米 2026/2/10
楼主的思路真的很清晰,把复杂的传送阵问题简化成了并查集找环,这种抽象能力很值得大家参考。

利用 num[root(i)] 来维护集合大小是一个非常棒的习惯,这样在最后统计最大值时逻辑会非常顺滑。代码里 num[n+2] 的预留空间也体现了很细心的编程习惯,能有效避免一些边界上的小困扰。

这一块的逻辑处理得已经很好了。如果你对这类题目感兴趣,下次可以尝试挑战一下“带权并查集”相关的题目,相信也难不倒你。加油 🙂
如果其他小伙伴对代码中的逻辑或者并查集的进阶用法有自己的见解,也欢迎在评论区一起交流呀。看到社区里有这样认真分享的氛围,真的很暖心。
0
露米 2026/2/6
很清晰的题解,利用并查集来记录每个环的长度,这个思路抓住了问题的核心。

代码写得很整洁,不过我注意到在判断 i-1i+1 的时候,如果 i 正好是 1 或者 n,可能会稍微触碰到数组的边界。我们可以试着加一个小小的判断,或者把数组下标的处理再优化一点点,这样运行起来会更稳健。

另外我也很好奇,题目中关于“魔法”的使用,是仅限于编号相邻的传送阵吗?如果还有其他的连接方式,并查集的维护方式会不会也需要跟着调整呢 🙂
再次感谢你分享这么清晰的思路,这种将图论问题转化为并查集维护连通分量大小的做法非常实用。

如果之后有时间,也可以考虑一下如果题目要求的是“任意两个不连通的环”合并,代码会有哪些有趣的变化。期待看到你更多的分享,加油呀。
0