1 条题解

  • 0
    @ 2025-8-24 21:40:09

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar KesdiaelKen
    **

    搬运于2025-08-24 21:40:09,当前版本为作者最后更新于2017-09-23 10:56:37,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    嗯……本人表示真的还不会toposort……所以,我就打了个奇奇怪怪的方法,居然也过了。

    下面介绍一下我在本题的经历——

    首先,我以为用set暴力判重能过,然而T了6个点。

    然后,我彻夜不眠,思考着本题。然后,我就想出来了,然后就过了……

    好吧,还是介绍一下思路。

    可以把这些关系看成一个有向图。对于任意一个节点进入,因为每个点的出度均为1,所以最多只能构成三种情况:1、一条链;2、一个环链;3、一条链连接着一个环链。因为这三种情况极为的简单,所以就可以如下处理:

    找任意一个点进入,记录到达每一个点所走过的遍数。当走到一个在这次查找中已经出现过的节点,即找到了一个环,用当前走到的步数减去在此节点原先记录的步数,便得到这个环的长度。由此搜遍所有点,找到这些环中最小长度的一个,并把它输出就可以了。

    而如果就这样去做,会TLE,所以得再加一点优化。对于一次查找环中,由于此次查找中至多只有一个环,而此环已经确定,所以再有外部的链介入此环,它的状态仍然不变。所以可以加个判断,如果走到了已经被查找过的节点,便直接跳出。

    下为代码,供大家参考:

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<string>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    int dx[300000];//存每一个人传话的对象
    bool visit[300000]={0},novisit[300000]={0};//visit存每次查找中被查到的点,而novisit存每次查找前,已经被查找过的点(及不用继续查找了)
    int bs[300000]={0};//每次查找中第一次到一个节点所经过的边数
    int minn=2e9;
    void dfs(int node,int num)
    {
        if(novisit[node])return;//不需要继续找了
        if(visit[node])//在此次查找中出现过
        {
            minn=min(minn,num-bs[node]);//形成一个环,取最小值
        }
        else
        {
            visit[node]=true;//在此次循环中经过
            bs[node]=num;//记录第一次到达时的步数
            dfs(dx[node],num+1);//搜索
            novisit[node]=true;//已经搜过
        }
    }
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&dx[i]);
        }
        for(int i=1;i<=n;i++)
        {
            dfs(i,0);//枚举全部节点
        }
        printf("%d",minn);//输出
        return 0;//时间复杂度O(n)
    }
    
    • 1

    信息

    ID
    1619
    时间
    1000ms
    内存
    125MiB
    难度
    3
    标签
    递交数
    0
    已通过
    0
    上传者