洛谷P2661 信息传递

https://www.luogu.org/fe/problem/P2661
一个含有n个点的图,图中每个点的出度为1(如果不考虑自环,那么有可能出现出度为0的情况)要求寻找图中最小的环。
做法:
在读取边时去除形成自环的边。然后遍历所有点,删除所有入度为0的点(删除一个点时它通往的下一个节点入度也相应减1,所以如果下一个节点入度变为0,那么也要删除)。然后剩余的所有点一定都属于某一个环的一部分。遍历所有的环,找到最小的环即可。
注意合理删点,并且避免重复遍历(所有在环上的点只需要被遍历一遍,遍历过的点不必再看)我先是因为只从入度判断一个点是否被删除,结果被卡内存(递归层数太多?),后来发现应当加一个标记数组,直接标记一个点是否有效。每次只删除入度为0且被标记为有效的点。删除操作就是把这个点标记为无效,将它抵达的下一个点入度减1。删点操作本身也是一个dfs,是否删除下一个点也要同时满足两个要求:1是下一个点入度为0,2是下一个点的标记仍然处于有效状态。只判断下一个点入度为0就去删除的话会导致重复删点,导致爆内存。

代码如下:

#include <bits/stdc++.h>

using namespace std;

int n;
int way[200000+1];
int ans = 0x7f7f7f7f;

int beginn;
int rd[200000+1];
bool isok[200000+1];

void dfs(int p,int t)
{
    isok[p] = 0;
    if(way[p] == beginn)
    {
        if(t < ans)
        {
            ans = t;
        }
        return;
    }
    else
    {
        dfs(way[p],t+1);
    }
}

void deletedfs(int p)
{
    isok[p] = 0;
    rd[way[p]] --;
    if(rd[way[p]] <= 0&&isok[way[p]] == 1)
    {
        deletedfs(way[p]);
    }
}

int main()
{
    scanf("%d",&n);
    memset(rd,0,sizeof(rd));
    memset(isok,1,sizeof(isok));
    for(int i = 1; i<=n; i++)
    {
        scanf("%d",way+i);
        if(way[i]!=i)
            rd[way[i]]++;
    }
    for(int i = 1; i<=n; i++)
    {
        if(rd[i] == 0&&isok[i] == 1)
        {
            deletedfs(i);
        }
    }
    for(int i = 1; i<=n; i++)
    {
        if(isok[i] == 1)
        {
            beginn = i;
            dfs(i,1);
        }
    }
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值