有向无环图求最长路

原文地址:http://www.geeksforgeeks.org/find-longest-path-directed-acyclic-graph/


给定一个带权有向无环图及源点S,在图中找出从S出发到图中其它所有顶点的最长距离。


对于一般的图,求最长路径并不向最短路径那样容易,因为最长路径并没有最优子结构的属性。实际上求最长路径属于NP-Hard问题。然而,对于有向无

环图,最长路径问题有线性时间的解。思路与通过使用拓扑排序在线性时间求最短路径[1]一样。


首先初始化到所有顶点的距离为负无穷大,到源点的距离为0,然后找出拓扑序。图的拓扑排序代表一个图的线性顺序。(图b是图a的一个线性表示)。

当找到拓扑序后,逐个处理拓扑序中的所有顶点。对于每个被处理的顶点,通过使用当前顶点来更新到它的邻接点距离。



图(b)中,到点s的距离初始化为0,到其它点的距离初始化为负无穷大,而图(b)中的边表示图(a)中边的权值。

图(c)中,求得从s到r的距离为负无穷。

图(d)中,求得s到t的最长距离为2,到x的最长距离为6。

图(e)至图(h)依次求得可达点间的最长距离。


下面是寻找最长路径的算法

  1.  初始化 dist[] = {NINF, NINF, ….}  ,dist[s] = 0 。s是源点,NINF表示负无穷。dist表示源点到其它点的最长距离。
  2.     建立所有顶点的拓扑序列。
  3.     对拓扑序列中的每个顶点u执行下面算法。 
              对u的每个邻接点v 
              if (dist[v] < dist[u] + weight(u, v)) ………………………dist[v] = dist[u] + weight(u, v) 

下面是C++的实现。

  1. // A C++ program to find single source longest distances in a DAG  
  2. #include <iostream>  
  3. #include <list>  
  4. #include <stack>  
  5. #include <limits.h>  
  6. #define NINF INT_MIN  
  7. using namespace std;  
  8.    
  9. //图通过邻接表来描述。邻接表中的每个顶点包含所连接的顶点的数据,以及边的权值。  
  10. class AdjListNode  
  11. {  
  12.     int v;  
  13.     int weight;  
  14. public:  
  15.     AdjListNode(int _v, int _w)  { v = _v;  weight = _w;}  
  16.     int getV()       {  return v;  }  
  17.     int getWeight()  {  return weight; }  
  18. };  
  19.    
  20. // Class to represent a graph using adjacency list representation  
  21. class Graph  
  22. {  
  23.     int V;    // No. of vertices’  
  24.    
  25.     // Pointer to an array containing adjacency lists  
  26.     list<AdjListNode> *adj;  
  27.    
  28.     // A function used by longestPath  
  29.     void topologicalSortUtil(int v, bool visited[], stack<int> &Stack);  
  30. public:  
  31.     Graph(int V);   // Constructor  
  32.    
  33.     // function to add an edge to graph  
  34.     void addEdge(int u, int v, int weight);  
  35.    
  36.     // Finds longest distances from given source vertex  
  37.     void longestPath(int s);  
  38. };  
  39.    
  40. Graph::Graph(int V) // Constructor  
  41. {  
  42.     this->V = V;  
  43.     adj = new list<AdjListNode>[V];  
  44. }  
  45.    
  46. void Graph::addEdge(int u, int v, int weight)  
  47. {  
  48.     AdjListNode node(v, weight);  
  49.     adj[u].push_back(node); // Add v to u’s list  
  50. }  
  51.    
  52. // 通过递归求出拓扑序列. 详细描述,可参考下面的链接。  
  53. // http://www.geeksforgeeks.org/topological-sorting/  
  54. void Graph::topologicalSortUtil(int v, bool visited[], stack<int> &Stack)  
  55. {  
  56.     // 标记当前顶点为已访问  
  57.     visited[v] = true;  
  58.    
  59.     // 对所有邻接点执行递归调用  
  60.     list<AdjListNode>::iterator i;  
  61.     for (i = adj[v].begin(); i != adj[v].end(); ++i)  
  62.     {  
  63.         AdjListNode node = *i;  
  64.         if (!visited[node.getV()])  
  65.             topologicalSortUtil(node.getV(), visited, Stack);  
  66.     }  
  67.    
  68.     // 当某个点没有邻接点时,递归结束,将该点存入栈中。  
  69.     Stack.push(v);  
  70. }  
// A C++ program to find single source longest distances in a DAG
#include <iostream>
#include <list>
#include <stack>
#include <limits.h>
#define NINF INT_MIN
using namespace std;
 
//图通过邻接表来描述。邻接表中的每个顶点包含所连接的顶点的数据,以及边的权值。
class AdjListNode
{
    int v;
    int weight;
public:
    AdjListNode(int _v, int _w)  { v = _v;  weight = _w;}
    int getV()       {  return v;  }
    int getWeight()  {  return weight; }
};
 
// Class to represent a graph using adjacency list representation
class Graph
{
    int V;    // No. of vertices’
 
    // Pointer to an array containing adjacency lists
    list<AdjListNode> *adj;
 
    // A function used by longestPath
    void topologicalSortUtil(int v, bool visited[], stack<int> &Stack);
public:
    Graph(int V);   // Constructor
 
    // function to add an edge to graph
    void addEdge(int u, int v, int weight);
 
    // Finds longest distances from given source vertex
    void longestPath(int s);
};
 
Graph::Graph(int V) // Constructor
{
    this->V = V;
    adj = new list<AdjListNode>[V];
}
 
void Graph::addEdge(int u, int v, int weight)
{
    AdjListNode node(v, weight);
    adj[u].push_back(node); // Add v to u’s list
}
 
// 通过递归求出拓扑序列. 详细描述,可参考下面的链接。
// http://www.geeksforgeeks.org/topological-sorting/
void Graph::topologicalSortUtil(int v, bool visited[], stack<int> &Stack)
{
    // 标记当前顶点为已访问
    visited[v] = true;
 
    // 对所有邻接点执行递归调用
    list<AdjListNode>::iterator i;
    for (i = adj[v].begin(); i != adj[v].end(); ++i)
    {
        AdjListNode node = *i;
        if (!visited[node.getV()])
            topologicalSortUtil(node.getV(), visited, Stack);
    }
 
    // 当某个点没有邻接点时,递归结束,将该点存入栈中。
    Stack.push(v);
}

  1. // 根据传入的顶点,求出到到其它点的最长路径. longestPath使用了  
  2. // topologicalSortUtil() 方法获得顶点的拓扑序。  
  3. void Graph::longestPath(int s)  
  4. {  
  5.     stack<int> Stack;  
  6.     int dist[V];  
  7.    
  8.     // 标记所有的顶点为未访问  
  9.     bool *visited = new bool[V];  
  10.     for (int i = 0; i < V; i++)  
  11.         visited[i] = false;  
  12.    
  13.     // 对每个顶点调用topologicalSortUtil,最终求出图的拓扑序列存入到Stack中。  
  14.     for (int i = 0; i < V; i++)  
  15.         if (visited[i] == false)  
  16.             topologicalSortUtil(i, visited, Stack);  
  17.    
  18.     //初始化到所有顶点的距离为负无穷  
  19.     //到源点的距离为0  
  20.     for (int i = 0; i < V; i++)  
  21.         dist[i] = NINF;  
  22.     dist[s] = 0;  
  23.    
  24.     // 处理拓扑序列中的点  
  25.     while (Stack.empty() == false)  
  26.     {  
  27.         //取出拓扑序列中的第一个点  
  28.         int u = Stack.top();  
  29.         Stack.pop();  
  30.    
  31.         // 更新到所有邻接点的距离  
  32.         list<AdjListNode>::iterator i;  
  33.         if (dist[u] != NINF)  
  34.         {  
  35.           for (i = adj[u].begin(); i != adj[u].end(); ++i)  
  36.              if (dist[i->getV()] < dist[u] + i->getWeight())  
  37.                 dist[i->getV()] = dist[u] + i->getWeight();  
  38.         }  
  39.     }  
  40.    
  41.     // 打印最长路径  
  42.     for (int i = 0; i < V; i++)  
  43.         (dist[i] == NINF)? cout << "INF ": cout << dist[i] << " ";  
  44. }  
// 根据传入的顶点,求出到到其它点的最长路径. longestPath使用了
// topologicalSortUtil() 方法获得顶点的拓扑序。
void Graph::longestPath(int s)
{
    stack<int> Stack;
    int dist[V];
 
    // 标记所有的顶点为未访问
    bool *visited = new bool[V];
    for (int i = 0; i < V; i++)
        visited[i] = false;
 
    // 对每个顶点调用topologicalSortUtil,最终求出图的拓扑序列存入到Stack中。
    for (int i = 0; i < V; i++)
        if (visited[i] == false)
            topologicalSortUtil(i, visited, Stack);
 
    //初始化到所有顶点的距离为负无穷
    //到源点的距离为0
    for (int i = 0; i < V; i++)
        dist[i] = NINF;
    dist[s] = 0;
 
    // 处理拓扑序列中的点
    while (Stack.empty() == false)
    {
        //取出拓扑序列中的第一个点
        int u = Stack.top();
        Stack.pop();
 
        // 更新到所有邻接点的距离
        list<AdjListNode>::iterator i;
        if (dist[u] != NINF)
        {
          for (i = adj[u].begin(); i != adj[u].end(); ++i)
             if (dist[i->getV()] < dist[u] + i->getWeight())
                dist[i->getV()] = dist[u] + i->getWeight();
        }
    }
 
    // 打印最长路径
    for (int i = 0; i < V; i++)
        (dist[i] == NINF)? cout << "INF ": cout << dist[i] << " ";
}

  1. // Driver program to test above functions  
  2. int main()  
  3. {  
  4.     // Create a graph given in the above diagram.  Here vertex numbers are  
  5.     // 0, 1, 2, 3, 4, 5 with following mappings:  
  6.     // 0=r, 1=s, 2=t, 3=x, 4=y, 5=z  
  7.     Graph g(6);  
  8.     g.addEdge(0, 1, 5);  
  9.     g.addEdge(0, 2, 3);  
  10.     g.addEdge(1, 3, 6);  
  11.     g.addEdge(1, 2, 2);  
  12.     g.addEdge(2, 4, 4);  
  13.     g.addEdge(2, 5, 2);  
  14.     g.addEdge(2, 3, 7);  
  15.     g.addEdge(3, 5, 1);  
  16.     g.addEdge(3, 4, -1);  
  17.     g.addEdge(4, 5, -2);  
  18.    
  19.     int s = 1;  
  20.     cout << "Following are longest distances from source vertex " << s <<" \n";  
  21.     g.longestPath(s);  
  22.    
  23.     return 0;  
  24. }  
// Driver program to test above functions
int main()
{
    // Create a graph given in the above diagram.  Here vertex numbers are
    // 0, 1, 2, 3, 4, 5 with following mappings:
    // 0=r, 1=s, 2=t, 3=x, 4=y, 5=z
    Graph g(6);
    g.addEdge(0, 1, 5);
    g.addEdge(0, 2, 3);
    g.addEdge(1, 3, 6);
    g.addEdge(1, 2, 2);
    g.addEdge(2, 4, 4);
    g.addEdge(2, 5, 2);
    g.addEdge(2, 3, 7);
    g.addEdge(3, 5, 1);
    g.addEdge(3, 4, -1);
    g.addEdge(4, 5, -2);
 
    int s = 1;
    cout << "Following are longest distances from source vertex " << s <<" \n";
    g.longestPath(s);
 
    return 0;
}

输出结果:
  1. 从源点1到其它顶点的最长距离  
  2. INF 0 2 9 8 10  
从源点1到其它顶点的最长距离
INF 0 2 9 8 10

时间复杂度:拓扑排序的时间复杂度是O(V+E).求出拓扑顺序后,对于每个顶点,通过循环找出所有邻接点,时间复杂度为O(E)。所以内部循环运行O(V+E)次。
因此算法总的时间复杂度为O(V+E)。

[1] http://www.geeksforgeeks.org/shortest-path-for-directed-acyclic-graphs/

<think>好的,我现在需要帮助用户解决关于有向无环图(DAG)最长通路算法的问题。首先,我得回忆一下DAG的最长路径问题的基础知识。用户提到了拓扑排序和动态规划,这可能是一个关键点。 根据引用[2],DAG的最长路径问题可以通过拓扑排序和动态规划来解决。虽然一般有向图的最长路径是NP难的,但DAG有线性时间的解法。这应该是因为拓扑排序后,节点的处理顺序确保了无环,从而可以使用动态规划。 接下来,我需要确认具体的算法步骤。根据引用中的信息,拓扑排序后,按照拓扑序处理每个节点,更新其相邻节点的最长路径值。每个节点的dist[v]表示从起点到v的最长路径,初始时可能需要设置为负无穷,起点设为0或者根据具体情况调整。 用户可能需要代码示例,所以我要考虑使用哪种语言。引用[1]提到了C++,但用户没有指定语言,可能Python更通用。不过,用户提供的示例中有Python代码,所以用Python可能更合适。 然后,我需要考虑如何实现拓扑排序。Kahn算法或者DFS都可以。Kahn算法通过入度表来排序,而DFS通过递归栈。这里可能需要选择一种方法。Kahn算法可能更直观,适合动态规划的处理。 接下来,动态规划部分。对于每个节点u,遍历其所有邻居v,如果dist[v] < dist[u] + weight(u, v),则更新dist[v]。这需要邻接表的表示,可能用字典或者列表来存储图的结构。 另外,引用[3]提到处理负权重边的情况,DAG的最短路径算法可以处理负权重,但最长路径可能不需要特别处理负权重,因为动态规划方法本身可以处理。不过最长路径中如果有正权重环的话,但DAG不存在环,所以没问题。 需要考虑用户是否需要具体的实现步骤,比如图的构建、拓扑排序的实现、动态规划的应用,以及如何处理多个起点的情况。比如,如果用户需要找到全局最长路径,可能需要将所有节点的dist初始化为负无穷,然后对每个节点作为起点运行算法?不过这样时间复杂度会很高。正确的做法是,在拓扑排序后,遍历所有可能的起点,或者直接在整个拓扑序列中处理,找到最大的dist值。 可能用户的问题是如何实现整个算法,所以需要给出具体步骤:构建图、拓扑排序、初始化dist数组、遍历拓扑序更新距离、最后找到最大值。 最后,生成相关问题,比如时间复杂度、如何处理特定情况、比较其他算法等,以帮助用户深入理解。 总结步骤: 1. 图的表示(邻接表) 2. 拓扑排序(Kahn算法或DFS) 3. 动态规划计算最长路径 4. 寻找所有节点中的最大值 需要确保代码正确,比如处理入度表和队列,以及更新距离的方式。同时,需要考虑权重是否为负数,但根据问题描述,可能默认权重可以是任意值,只要没有环。</think>### 有向无环图(DAG)最长通路算法实现 有向无环图(DAG)的最长通路问题可通过**拓扑排序+动态规划**解决,时间复杂度为$O(V+E)$,其中$V$为顶点数,$E$为边数[^2]。以下是具体实现步骤: --- #### **1. 算法步骤** 1. **拓扑排序** 生成顶点的线性序列,保证所有有向边从序列前驱指向后继。 2. **动态规划** 按拓扑序遍历顶点,用$dist[v]$记录从任意起点到$v$的最长路径长度: $$dist[v] = \max_{(u,v) \in E} \{dist[u] + weight(u,v)\}$$ 初始时$dist[v] = 0$(若需全局最长路径,需遍历所有起点)。 --- #### **2. Python实现代码** ```python from collections import deque def longest_path_dag(vertices, edges): # 初始化邻接表和入度表 adj = {v: [] for v in vertices} in_degree = {v: 0 for v in vertices} for u, v, w in edges: adj[u].append((v, w)) in_degree[v] += 1 # 拓扑排序(Kahn算法) queue = deque([v for v in vertices if in_degree[v] == 0]) topo_order = [] while queue: u = queue.popleft() topo_order.append(u) for v, _ in adj[u]: in_degree[v] -= 1 if in_degree[v] == 0: queue.append(v) # 动态规划计算最长路径 dist = {v: 0 for v in vertices} # 若允许负权重,初始化为负无穷 for u in topo_order: for v, w in adj[u]: if dist[v] < dist[u] + w: dist[v] = dist[u] + w return max(dist.values()) if dist else 0 # 示例调用 vertices = ['A', 'B', 'C', 'D'] edges = [('A', 'B', 3), ('A', 'C', 2), ('B', 'D', 5), ('C', 'D', 4)] print("最长路径长度:", longest_path_dag(vertices, edges)) # 输出:3+5=8(A→B→D) ``` --- #### **3. 关键点说明** - **拓扑排序必要性**:确保动态规划按无环顺序更新,避免循环依赖[^2]。 - **负权重处理**:若边权可为负,初始化$dist[v]$为负无穷,并将起点设为0。 - **全局最长路径**:需遍历所有可能的起点,或直接取$dist$中的最大值(如示例所示)。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值