2024年3月20日-暑期实习-第三题(300分)-循环依赖

在线评测链接

题目描述

给定一组元素,及其依赖关系,一个元素可以依赖于多个元素(不包括自己,被依赖元素不会重复)一个元素也可被多个元素依赖。假定总是存在唯一的循环依赖,请输出该循环依赖。

输入描述

第一行一个正整数 N N N,表示依赖关系的个数。

接下来每一行表示一个依赖关系,是由空格分割的多个正整数,第一个数 n n n表示后面有 n n n个元素,第二个数为元素编号 a a a,后面多个数为 a a a依赖的元素编号,任意元素 i i i满足 0 < t < 10000 0\lt t\lt 10000 0<t<10000

输出描述

一串数字,代表这个循环依赖,从最小元素编号开始,按照依赖关系依次输出,以最小元素结束。

样例

输入

3
3 1 2 5
3 2 3 4
2 3 1

输出

1 2 3 1

说明

  1. 元素1依赖于2,5
  2. 元素2依赖于3,4
  3. 元素3依赖于1

思路

简化题意:给出若干条边组成的有向图,找出其中唯一的环,保证环一定存在。
直接 dfs ,遍历完点 u 还未找到环,则说明点 u 不在这个环上。
当某个点已经被遍历过一次,且在 dfs 过程中再次被遍历过,说明遍历完了一整个环。
具体实现上使用一个栈来存储 dfs 过程中遍历到的点,如果一个点 u 被遍历到了两次,则说明找到了这个环,且 u 是这个环上的点。
只需要依次弹出栈中的点,直至弹出 u 为止,这就是环中所有的点。
需要注意的是:

  • 弹出栈中的点获得的是一个倒序的环,需要反转一下
  • 如果一个点在遍历完毕后仍然未结束,说明这个点不在环上,需要从栈中弹出

时间复杂度:O(m), m 为所有的边的数量

AC代码

  • python
MAX = 10000
n = int(input())
g = [[] for i in range(MAX)]
vis = [0] * MAX
in_stk = [0] * MAX

for i in range(n):
    a = list(map(int, input().split()))
    for j in range(2, len(a)):
        g[a[1]].append(a[j])

stk = []
t = []
def dfs(u):
    # 这个点在本次遍历或者之前的某次遍历中被遍历过了
    if vis[u]:
        # 如果在栈中,说明本次遍历中被遍历过了
        if len(stk) > 0 and in_stk[u]:
            # 弹出元素直至弹出 u
            while len(stk) > 0 and stk[-1] != u:
                in_stk[stk[-1]] = 0
                t.append(stk[-1])
                stk.pop()
            in_stk[u] = 0
            stk.pop()
            t.append(u)
            # 找到环了,根据题意有且仅有一个环,直接退出
            return True
        else:
            return False
    vis[u] = 1
    stk.append(u)
    in_stk[u] = 1
    for v in g[u]:
        if dfs(v):
            return True
    in_stk[u] = 0
    # 这个点不在环上,需要从栈中弹出
    stk.pop()
    return False


cnt = 0
for i in range(1, MAX):
    # 依次遍历所有还未被遍历过的点
    if not vis[i]:
        if dfs(i):
            cnt += 1

if cnt > 1:
    print("oh no")

if len(t) > 0:
    # 弹出栈中的点获得的是一个倒序的环,需要反转一下
    t.reverse()
    # 题目中说了从最小的元素开始输出这个环
    minv = min(t)
    i = 0
    while i < len(t):
        if t[i] == minv:
            break
        i += 1
    ans = t[i:] + t[:i]
    ans.append(t[i])
    print(*ans)


  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

塔子哥学算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值