📜  有向无环图中的最长路径|动态规划(1)

📅  最后修改于: 2023-12-03 15:26:29.816000             🧑  作者: Mango

有向无环图中的最长路径-动态规划

有向无环图(DAG)是一种不含有环的有向图。在DAG中,所有节点可以被排成一个线性序列,使得对于每一条有向边(u, v),顶点u在序列中出现在顶点v的前面。这种无环性质使得DAG具有很多优良的性质,例如拓扑排序和最短路问题都可以在DAG上被高效地解决。

本文将介绍如何利用动态规划来求解DAG中的最长路径问题。最长路径问题即在有向图中找出一条路径,使得该路径上各个点权的和最大。于是问题可以被形式化地描述为:

给定有向无环图G=(V, E), 以及每个节点v的权值w(v),找到一条最长路径P=(v1,v2,...,vn),使得路径上所有节点的权值之和最大。

动态规划思路

动态规划(DP)是一种用于解决优化问题的算法思想。它将问题分解为大量重叠子问题,通过保存已经求解的子问题的结果,避免了重复的计算。DP解决问题的核心思想在于:对于给定的问题,定义状态,设计状态转移方程,利用已解决的子问题推导出大问题的解。

对于本问题,我们可以利用DP来解决最长路径问题。具体地,首先定义一个状态数组L[],其中L[i]表示从节点i出发的最长路径长度。初始化L[i]=w[i],即节点i的最长路径等于节点权值。接着,对于图中任意一条边(u, v),我们有以下状态转移方程:

L[v] = max(L[v], L[u] + w[v])

上述方程的含义在于,“从v出发的最长路径”可以通过“从u出发的最长路径”和“边(u,v)的权值”两者之和得到。因此,我们可以通过遍历有向无环图中的所有节点,使用上述方程,求得每个节点出发的最长路径。最后,遍历L数组找出其中的最大值即可。

伪代码

根据DP思想,我们可以将求解最长路径问题转化为以下伪代码:

// 初始化状态数组L,L[i]表示从节点i出发的最长路径长度。
for i in range(V):
    L[i] = w[i]

// 遍历DAG中的所有节点,更新状态数组L。
for u in topological_sort(G):
    for v in neighbors(u):
        L[v] = max(L[v], L[u] + weight(u, v))

// 找出最长路径的长度。
L_max = max(L)

其中,topological_sort(G)表示对DAG进行拓扑排序,neighbors(u)表示节点u的后继节点集合,weight(u, v)表示边(u, v)的权重。

代码实现

下面是该算法在Python中的实现代码:

def longest_path(G, w):
    # 初始化状态数组L,L[i]表示从节点i出发的最长路径长度。
    L = {i: w[i] for i in G.nodes()}

    # 遍历DAG中的所有节点,更新状态数组L。
    for u in nx.topological_sort(G):
        for v in G.successors(u):
            L[v] = max(L[v], L[u] + w[v])

    # 找出最长路径的长度。
    return max(L.values())

其中,G表示有向无环图(使用networkx库实现),w是一个字典类型,表示节点的权重。

算法复杂度

该算法的时间复杂度与DAG中节点和边的数量成正比。具体地,我们需要进行一次拓扑排序(O(V+E))来确定遍历的顺序,然后需要遍历所有的节点和边(O(V+E)),对于每个节点需要遍历其所有的后继节点(最坏情况下,每个节点有O(V)个后继节点),因此总时间复杂度为O(V^2 + VE)或O(V+E)。

另外,该算法需要额外使用一个状态数组L,空间复杂度为O(V)。

示例

下面给出一个示例图和它的最长路径。图中,每个节点的编号和权值分别用括号表示。

DAG

该图的最长路径为6→5→4↓→3↑→2→1,权值之和为26。

我们可以使用前面提到的Python代码来验证结果。首先,定义图结构和节点权值:

import networkx as nx

G = nx.DiGraph()
G.add_edges_from([(1, 2), (1, 3)])
G.add_edge(2, 3)
G.add_edges_from([(2, 4), (2, 5)])
G.add_edge(3, 5)
G.add_edges_from([(4, 5), (4, 6)])
G.add_edge(5, 6)

w = {1: 5, 2: 3, 3: 2, 4: 6, 5: 4, 6: 1}

接着,调用longest_path函数计算最长路径:

L_max = longest_path(G, w)
print('Longest path length:', L_max)

最后将输出:

Longest path length: 26
结论

本文介绍了如何利用动态规划求解DAG中的最长路径问题。该算法可以在O(V^2 + VE)或O(V+E)时间复杂度内解决问题,空间复杂度为O(V)。此外,我们还给出了Python代码实现,并使用一个示例图验证了其正确性。