📅  最后修改于: 2023-12-03 15:10:38.214000             🧑  作者: Mango
在计算机科学中,有向无环图(DAG)是一种有向图,其中不存在一个点的有向路径可以回到该点本身,从而得名为“无环”。在许多应用中,我们经常需要计算有向无环图中从源到目的地的路径个数。例如,在工作流、计划安排和遗传学中,我们需要计算从一些初始状态到一些目标状态的所有可能路径。
给定一个有向无环图 G,和两个顶点 s 和 t,我们需要计算从 s 到 t 的所有路径数。
定义 dp[i] 表示从 s 到 i 的所有路径数。那么,dp[t] 就是我们要求的答案。
最简单的思路是,对于每一个 i,我们枚举其所有入边的起点 j,如果 j 可以到达 s,则有 dp[i] = dp[i] + dp[j]。
其状态转移方程如下:
dp[i] = 0
for each (j, i) in E:
if (j can reach s):
dp[i] = dp[i] + dp[j]
这是一个时间复杂度为 O(V+E) 的算法,其中 V 和 E 分别表示图的顶点数和边数。
我们可以将上述方法改为记忆化搜索的形式,以减少无效递归调用的次数。
我们定义一个 memo 数组,表示从 s 到每个顶点的所有路径数。在搜索到一个顶点 i 时,如果 memo[i] 已经计算过,就不需要再次计算。否则,我们遍历所有从 i 出发的边,并累加 dp[j],其中 j 是 i 的直接后继。
其递推公式如下:
dfs(i):
if memo[i] != -1:
return memo[i]
memo[i] = 0
for each (i, j) in E:
memo[i] += dfs(j)
return memo[i]
时间复杂度为 O(V+E),空间复杂度为 O(V)。
使用拓扑排序可以更高效地计算有向无环图中从源到目的地的路径数。
我们对 DAG 进行拓扑排序,得到一个顶点列表 v,其中 s 是第一个顶点,t 是最后一个顶点。对于列表中的每个顶点 i,我们计算以 i 为起点,以 t 为终点的所有路径数。这是因为 i 的后继可能已经被计算,因此路径数可以通过加法原理相加得到。
具体地,我们定义 cnt[i] 表示以 i 为起点,以 t 为终点的所有路径数。显然,对于 t,cnt[t]=1。然后我们遍历顶点列表 v,从后往前计算 cnt[i]。
其递推公式如下:
cnt[i] = 0
for each (i, j) in E:
cnt[i] += cnt[j]
时间复杂度为 O(V+E),空间复杂度为 O(V)。
本文介绍了三种计算有向无环图中从源到目的地的路径数的方法:动态规划、记忆化搜索和拓扑排序。这些方法的时间复杂度都为 O(V+E),但拓扑排序的常数因子更小,因此在实践中更为优秀。