ACM 在有向无环图中求最长路径

本文介绍了一种针对有向无环图(DAG)的最长路径算法,利用拓扑排序和动态规划思想,在O(V+E)时间内求解从指定源点到其他各顶点的最长路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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)依次求得可达点间的最长距离。

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

下面是C++的实现。

#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);  
}  


[cpp] view plain copy
// 根据传入的顶点,求出到到其它点的最长路径. 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] << " ";  
}  

[cpp] view plain copy
// 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到其它顶点的最长距离  
INF 0 2 9 8 10  

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值