题解 P2661 【信息传递】

· · 题解

看到很多大佬写并查集 的写法,但很多大佬都写的很麻烦,感觉并查集可以不压行写的很短(蒟蒻太弱不敢压行)

显然,对于有向图,如果考虑从每一个点出发,用DFS求出它到自己的距离,如果遍历了N个点仍未便利到自己,则该点不在环中。但是这样做的复杂度非常感人……如果没有分析错的话最坏应该是O(n^2m)。

聪明的我们又发现,此题两个小破孩之间没有权值,所以只要写一个普通并查集就可以解决这个问题。

只要先打一段并查集模板(没有路径压缩优化的

inline int find(int x) //正常的并查集
{
    if (fa[x]!=x)
      return find(fa[x]);
    else
      return x;
}

然后在添加一个变量deep,用来记录此时遍历了几个小盆友了

inline int find(int x,int &deep) //一边遍历,一边记录此时是第几轮
{
    deep++;//记录深度(第几个人,考虑到并查集是棵树,所以可以记成deep)
    if (fa[x]!=x)
      return find(fa[x],deep);
    else
      return x;
}

然后开始遍历,记得每次开始前先讲deep置零(用ans来记录全局最小值)很多大佬用并查集时都开了数组记录环的长度,其实不用这么麻烦。

for (int i=1;i<=n;i++) 
    {
        int deep=0,now;
        cin>>now;//当前这个小盆友传递对象是谁
        if(find(now,deep)==i)//如果又遍历到他自己,就是说他在这个环里
          ans=min(ans,deep);//记录当前环的长度
        else
          fa[i]=now;
    }

然后完美结束~~

但并查集求最小环不是很快,机房测试时10个点跑了1.3S,同机房红名大佬用拓扑+DFS用了0.37S,我太弱了,但好处是代码短。。。。

完整代码:

前面说的很清楚了,所以代码不带解释!!!

#include <iostream>
#include <cstdio>
using namespace std;
int n, fa[1000001],ans=121432423;
int find(int x,int &deep) 
{
    deep++;
    if (fa[x]!=x)
      return find(fa[x],deep);
    else
      return x;
}
int main () 
{
    freopen("message.in","r",stdin);
    freopen("message.out","w",stdout);
    cin>>n;
    for(int i=1;i<=n;i++);
      fa[i]=i;
    for (int i=1;i<=n;i++) 
    {
        int deep=0,now;
        cin>>now;
        if(find(now,deep)==i)
          ans=min(ans,deep);
        else
          fa[i]=now;
    }
    cout<<ans<<endl;
    return 0;
}

据说抄题解会变棕