第一次看见这个问题是 《算法导论》Chapter 15 动态规划课后思考题15-1的题目。
本文主要参考:http://www.geeksforgeeks.org/find-longest-path-directed-acyclic-graph/
给定一个有向图(Directed Acyclic Graph)以及一个起点s,求该图中s点到其余所有点的最长路径。
正如算法导论中提到的那样,给定有向图的最长路径问题并不如有向图最短路径那么简单,因为它不具备 最优子结构(Optimal Substructure Property)。实际上,该问题是NP-Hard问题。
但是,有向无环图的最长路径问题确有一个线性时间的解。这种思想和解决DAG最短路径问题相似,采用拓扑排序(Topological Sorting )。
我们将求得DAG的拓扑序列,那么对于拓扑序列,就可以用动态规划来处理。由拓扑序列的性质可以断定在拓扑序列该问题具有最优子结构。(当前问题的解包含子问题的解)
我们定义dist[v]为起点s到v的最长路径,那么递归关系为:
dist[v] = max{dist[pre] + weight(pre->v)},pre是一条指向v的边的起点
这里我们从左到右遍历拓扑序列,对于当前点i,更新其邻接点的dist。
需要强调的是这里的拓扑排序的实现方式是基于Dfs,需要掌握。
#include
#include
#include
#include
#include
#include
#include
//#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
//定义邻接点类
class AdjListNode {
int val;
int weight;
public:
AdjListNode(int _v, int _w) {
val = _v;
weight = _w;
}
int getV() { return val; }
int getWeight() { return weight; }
};
//定义图类
class Graph {
int num_v;
list *adj;
void TopologicalSortUtil(int v, bool visited[], stack<int> &Stack);
public:
Graph(int v);
void addEdge(int u, int v, int weight);
void LongestPath(int s);
};
Graph::Graph(int v) {
num_v = v;
adj = new list [v];
}
//Dfs求其拓扑排序
void Graph::TopologicalSortUtil(int v, bool visited[], stack<int> &Stack) {
visited[v] = true;
list ::iterator i;
for (i = adj[v].begin(); i != adj[v].end(); i++) {
if (visited[(*i).getV()] == true) continue;
TopologicalSortUtil((*i).getV(), visited, Stack);
}
Stack.push(v);
}
void Graph::addEdge(int u, int v, int weight) {
AdjListNode next(v, weight);
adj[u].push_back(next);
}
void Graph::LongestPath(int s) {
bool *visited = new bool[num_v];
int *dist = new int[num_v];
stack<int> Stack;
for (int i = 0; i < num_v; i++) {
visited[i] = false;
dist[i] = INT_MIN;
}
//初始化所有dist为INT_MIN,然后dist[s] = 0.
dist[s] = 0;
//求其拓扑序列
for (int i = 0; i < num_v; i++) {
if (!visited[i]) {
TopologicalSortUtil(i, visited, Stack);
}
}
while (!Stack.empty()) {
int u = Stack.top();
Stack.pop();
if (dist[u] == INT_MIN) continue;//如果此时仍不可达,则跳过该点
list ::iterator i;
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 < num_v; i++) {
if (i) printf(" ");
(dist[i] == INT_MIN) ? printf("INF") : printf("%d", dist[i]);
}
}
int main() {
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 distacnes from soucre vertex " << s << "\n";
g.LongestPath(s);
return 0;
}
写这篇博文目的:
1.Dfs外加一个for循环求DAG的拓扑排序。
2.很少写这种面向对象的代码,所以写一写练习一下。