在线评测链接
题目描述
给定一组元素,及其依赖关系,一个元素可以依赖于多个元素(不包括自己,被依赖元素不会重复)一个元素也可被多个元素依赖。假定总是存在唯一的循环依赖,请输出该循环依赖。
输入描述
第一行一个正整数 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依赖于2,5
- 元素2依赖于3,4
- 元素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)