拓扑排序和floyed算法
1、拓扑排序:
见链接
2、 floyed算法
见链接
例题:
下面两道例题,重点看如何使用拓扑排序来解答,当然也可以使用floyed算法预计算
有一组 n 个人作为实验对象,从 0 到 n - 1 编号,其中每个人都有不同数目的钱,以及不同程度的安静值(quietness)。为了方便起见,我们将编号为 x 的人简称为 "person x "。
给你一个数组 richer ,其中 richer[i] = [ai, bi] 表示 person ai 比 person bi 更有钱。另给你一个整数数组 quiet ,其中 quiet[i] 是 person i 的安静值。richer 中所给出的数据 逻辑自恰(也就是说,在 person x 比 person y 更有钱的同时,不会出现 person y 比 person x 更有钱的情况 )。
现在,返回一个整数数组 answer 作为答案,其中 answer[x] = y 的前提是,在所有拥有的钱肯定不少于 person x 的人中,person y 是最安静的人(也就是安静值 quiet[y] 最小的人)。
解答:
拓扑排序
如果 ai比 bi更有钱,我们从 ai向 bi连一条有向边。这样得到的是一张有向无环图,因此我们从图上任意一点(设为 x)出发,沿着有向边所能访问到的点,拥有的钱都比 x 少。这意味着我们可以在计算出 answer[x] 后,用answer[x] 去更新 xx 所能访问到的点的 answer 值。
要实现这一算法,我们可以将每个answer[x] 初始化为 x,然后对这张图执行一遍拓扑排序,并按照拓扑序去更新 x 的邻居的 answer 值。通过这一方式我们就能将answer[x] 的值「传播」到 x 所能访问到的点上
1 class Solution(object): 2 def loudAndRich(self, richer, quiet): 3 """ 4 :type richer: List[List[int]] 5 :type quiet: List[int] 6 :rtype: List[int] 7 """ 8 n = len(quiet) 9 graph = [[] for _ in range(n)] 10 res = [i for i in range(n)] 11 ind = [0 for _ in range(n)] 12 13 for u, v in richer: 14 graph[u].append(v) 15 ind[v] += 1 16 17 queue = deque([i for i in range(n) if ind[i] == 0]) 18 19 while queue: 20 u = queue.popleft() 21 for v in graph[u]: 22 if quiet[res[u]] < quiet[res[v]]: 23 res[v] = res[u] 24 ind[v] -= 1 25 if ind[v] == 0: 26 queue.append(v) 27 return res
floyed,预计算解法
当然, 还是暴力打表法更直接。过程
- 构建一个图表示从i到j是否可达,默认为false
- 构建方式
- 根据所给边来直接构建
- 基于中间k点即Floyed方式来判断是否可达
注意,这题只是给个思路,暴力会超时,至少py会
1 class Solution(object): 2 def loudAndRich(self, richer, quiet): 3 """ 4 :type richer: List[List[int]] 5 :type quiet: List[int] 6 :rtype: List[int] 7 """ 8 n = len(quiet) 9 dp = [[False for _ in range(n)] for _ in range(n)] 10 for i, j in richer: 11 dp[j][i] = True 12 13 for i in range(n): 14 dp[i][i] = True 15 16 for mid in range(n): 17 for i in range(n): 18 for j in range(n): 19 dp[i][j] = dp[i][mid] and dp[mid][j] or dp[i][j] 20 21 res = [] 22 23 for i in range(n): 24 minNum = float('inf') 25 ind = i 26 for j in range(n): 27 if dp[i][j]: 28 if minNum > quiet[j]: 29 minNum = quiet[j] 30 ind = j 31 res.append(ind) 32 33 return res
你总共需要上 n 门课,课程编号依次为 0 到 n-1 。
有的课会有直接的先修课程,比如如果想上课程 0 ,你必须先上课程 1 ,那么会以 [1,0] 数对的形式给出先修课程数对。
给你课程总数 n 和一个直接先修课程数对列表 prerequisite 和一个查询对列表 queries 。
对于每个查询对 queries[i] ,请判断 queries[i][0] 是否是 queries[i][1] 的先修课程。
请返回一个布尔值列表,列表中每个元素依次分别对应 queries 每个查询对的判断结果。
注意:如果课程 a 是课程 b 的先修课程且课程 b 是课程 c 的先修课程,那么课程 a 也是课程 c 的先修课程。
拓扑排序
构造拓扑排序,在构造过程中更新二维数组即可
1 class Solution: 2 def checkIfPrerequisite(self, numCourses: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]: 3 """ 4 :type numCourses: int 5 :type prerequisites: List[List[int]] 6 :type queries: List[List[int]] 7 :rtype: List[bool] 8 """ 9 graph = [[] for _ in range(numCourses)] 10 ind = [0 for _ in range(numCourses)] 11 12 connected = [[False for _ in range(numCourses)] for _ in range(numCourses)] 13 14 for i in range(numCourses): 15 connected[i][i] = True 16 17 for u, v in prerequisites: 18 graph[u].append(v) 19 ind[v]+=1 20 21 q = deque([i for i in range(numCourses) if ind[i] == 0]) 22 23 topo = [] 24 25 while q: 26 cur = q.popleft() 27 topo.append(cur) 28 for i in graph[cur]: 29 ind[i] -= 1 30 31 # 如果 j 到 cur 有链接 而 cur 到 i 有链接 所以 j 到 i 有链接 32 for j in topo: 33 if connected[j][cur]: 34 connected[j][i] = True 35 36 if ind[i] == 0: 37 q.append(i) 38 39 res = [] 40 for i, j in queries: 41 res.append(connected[i][j]) 42 43 return res
floyed预计算解法、
过程同上
1 class Solution: 2 def checkIfPrerequisite(self, numCourses: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]: 3 """ 4 :type numCourses: int 5 :type prerequisites: List[List[int]] 6 :type queries: List[List[int]] 7 :rtype: List[bool] 8 """ 9 dp = [[False for _ in range(numCourses)] for _ in range(numCourses)] 10 11 for r, c in prerequisites: 12 dp[r][c] = True 13 14 for mid in range(numCourses): 15 for i in range(numCourses): 16 for j in range(numCourses): 17 if dp[i][mid] and dp[mid][j]: 18 dp[i][j] = True 19 20 res = [] 21 for q, p in queries: 22 res.append(dp[q][p]) 23 24 return res