关键路径求解与AOE

原文链接

前言:首先关键路径是针对DAG图来说的,我们通常用AOE网来表示一个工程的进行过程,AOV网可以转换为AOE网,AOE网是没有环的,通常关键路径求解需要弄清楚以下四个概念:

事件最早发生时间Ve[u]、事件最晚发生时间Vl[u]

活动最早发生时间e[r]、活动最晚发生时间l[r]

在AOE网(Activity On Edge,用带权的边表示活动,用顶点表示事件的有向图)中,【其实一个事件(顶点)仅表示一个中介状态】首先,我们要求出每个事件(顶点)的最早发生时间Ve[u]和最晚发生时间Vl[u],“事件最早发生时间”可用拓扑排序来进行求解;而事件的最晚发生时间可以用逆拓扑来求解。以上两个求解出来后,就可以计算活动最早发生时间e[r]和活动最晚发生时间l[r]了,他们的关系如下:

e[r]=Ve[u]

l[r]+length[r]=Vl[v]

代码如下:

扫描二维码关注公众号,回复: 9445718 查看本文章

    
    
  1. #include"stdafx.h"
  2. #include<cstdio>
  3. #include<stack>
  4. #include<algorithm>
  5. #include<cmath>
  6. #include<iostream>
  7. #include<vector>
  8. #include<queue>
  9. using namespace std;
  10. const int maxn = 500;
  11. int Vertexnum, Edgenum;
  12. struct node {
  13. int v;
  14. int weight;
  15. };
  16. vector<node> adj[maxn]; //邻接表存储DAG图
  17. int innode[maxn] = { 0 }; //记录每个结点的入度
  18. queue< int> q;
  19. stack< int> s; //运用一个栈,到时候实现逆拓扑
  20. int Ve[maxn]; //事件发生的最早时间
  21. int Vl[maxn]; //事件发生的最晚时间
  22. bool topologicalsort() { //拓扑排序
  23. for ( int i = 0; i < Vertexnum; i++) { //将所有入度为0的点加入到队列中
  24. if (innode[i]== 0) {
  25. q.push(i);
  26. }
  27. }
  28. while (!q.empty()) {
  29. int u = q.front(); //取队首元素
  30. q.pop();
  31. s.push(u); //进入栈
  32. for ( int i = 0; i < adj[u].size(); i++) { //进行入度更新操作
  33. int v = adj[u][i].v;
  34. innode[v]--;
  35. if (innode[v] == 0) { //如果入度为0的点,则入队
  36. q.push(v);
  37. }
  38. if (Ve[u] + adj[u][i].weight > Ve[v]) { //计算事件最早发生时间
  39. Ve[v] = Ve[u] + adj[u][i].weight;
  40. }
  41. }
  42. }
  43. if (s.size() == Vertexnum) return true;
  44. else return false; //否则拓扑失败,说明此图是存在环,不是DAG图
  45. }
  46. void antitopologicalsort() { //逆拓扑求事件最晚发生时间
  47. while (!s.empty()) {
  48. int u = s.top();
  49. s.pop();
  50. for ( int i = 0; i < adj[u].size(); i++) { //计算事件最晚发生时间
  51. int v = adj[u][i].v;
  52. if (Vl[v] - adj[u][i].weight<Vl[u]) {
  53. Vl[u] = Vl[v] - adj[u][i].weight;
  54. }
  55. }
  56. }
  57. }
  58. void criticalpath() { //关键路径
  59. fill(Ve, Ve + maxn, 0); //事件最早发生时间初始化
  60. if (topologicalsort() == false) return; //拓扑失败
  61. fill(Vl, Vl + maxn, Ve[Vertexnum - 1]); //事件最晚发生时间初始化
  62. antitopologicalsort();
  63. cout << "关键路径长度:" << Ve[Vertexnum - 1] << "\n"; //输出关键路径长度
  64. for ( int u = 0; u < Vertexnum; u++) { //遍历所有的边
  65. for ( int i = 0; i < adj[u].size(); i++) {
  66. int v = adj[u][i].v; //边的两个端点分别是u和v
  67. int e = Ve[u]; //活动最早发生时间
  68. int l = Vl[v] - adj[u][i].weight; //活动最晚发生时间
  69. if (e == l) {
  70. cout << u << "->" << v << "\n"; //输出关键路径
  71. }
  72. }
  73. }
  74. }
  75. int main() {
  76. cin >> Vertexnum >> Edgenum;
  77. int start, end, weight;
  78. for ( int i = 0; i < Edgenum; i++) {
  79. cin >> start >> end >> weight;
  80. node N;
  81. N.v = end;
  82. N.weight = weight;
  83. innode[end]++; //入度加1
  84. adj[start].push_back(N);
  85. }
  86. criticalpath(); //关键路径
  87. return 0;
  88. }

运行结果如下:



    补充:其实求AOE网中的关键路径就是求DAG最长路路径,所以对于以上求DAG最长路还有一种更简单的办法:利用动态规划思想去解决,令dp[i]表示以i为源点的最长路,如果要求dp[i],就必须求出以源点i出发能够到达的顶点j的dp[j],而dp[j]的求解又是一样的思路,其中DAG图中出度为0的点v,他的dp[v]就是为0了,很显然,求解dp数组可以利用递归去求解。当求出所有的dp[i],其中dp值最大的就是DAG图的最长路径长度,代码如下:

以下算法实现的功能是:求出最长路径长度和输出最长路径序列


    
    
  1. #include"stdafx.h"
  2. #include<cstdio>
  3. #include<stack>
  4. #include<algorithm>
  5. #include<cmath>
  6. #include<iostream>
  7. #include<vector>
  8. #include<queue>
  9. using namespace std;
  10. const int maxn = 500;
  11. int Vertexnum, Edgenum;
  12. struct node {
  13. int v;
  14. int weight;
  15. };
  16. vector<node> adj[maxn]; //邻接表存储DAG图
  17. int outnode[maxn] = { 0 }; //记录每个顶点的出度
  18. int dp[maxn];
  19. void init() { //dp数组初始化
  20. fill(dp, dp + maxn, -1);
  21. for ( int i = 0; i < Vertexnum; i++) {
  22. if (outnode[i] == 0) { //初始化出度为0的dp为0
  23. dp[i] = 0;
  24. }
  25. }
  26. }
  27. vector< int> sumpath[maxn]; //sumpath[i]数组表示:以i为源点的DAG最长路路径序列
  28. int path[maxn]; //存放DAG最长路路径序列
  29. int DFS(int index) { //递归求解dp数组
  30. if (dp[index] == 0) return dp[index]; //到达递归边界
  31. for ( int i = 0; i < adj[index].size(); i++) {
  32. int v = adj[index][i].v;
  33. int weight = adj[index][i].weight;
  34. int distv = DFS(v);
  35. if (dp[index] < distv + weight) {
  36. dp[index] = distv + weight;
  37. path[index] = v; //顶点index的后继结点是v(和Dijkstra算法记录前驱结点的思路差不多)
  38. }
  39. }
  40. return dp[index];
  41. }
  42. int main() {
  43. cin >> Vertexnum >> Edgenum;
  44. int start, end, weight;
  45. for ( int i = 0; i < Edgenum; i++) {
  46. cin >> start >> end >> weight;
  47. node N;
  48. N.v = end;
  49. N.weight = weight;
  50. outnode[start]++; //更新结点的出度
  51. adj[start].push_back(N);
  52. }
  53. init(); //初始化
  54. for ( int i = 0; i < Vertexnum; i++) {
  55. path[i] = i; //初始化每个结点的后继结点为自身
  56. }
  57. int maxdp = -1; //记录最大的dp[i]的值
  58. int longestindex; //表示DAG最长路路径是以longestindex为源点
  59. for ( int i = 0; i < Vertexnum; i++) {
  60. int temp = DFS(i);
  61. sumpath[i].push_back(i);
  62. int j = i;
  63. while (path[j] != j) {
  64. j = path[j];
  65. sumpath[i].push_back(j);
  66. }
  67. if (maxdp < temp) {
  68. maxdp = temp;
  69. longestindex = i;
  70. }
  71. }
  72. cout << "关键路径长度:" << maxdp << "\n";
  73. cout << "关键路径序列为:";
  74. for ( int i = 0; i < sumpath[longestindex].size(); i++) { //输出以longestindex为源点的DAG最长路
  75. cout << sumpath[longestindex][i];
  76. }
  77. return 0;
  78. }

运行结果如下:



发布了37 篇原创文章 · 获赞 1 · 访问量 2739

前言:首先关键路径是针对DAG图来说的,我们通常用AOE网来表示一个工程的进行过程,AOV网可以转换为AOE网,AOE网是没有环的,通常关键路径求解需要弄清楚以下四个概念:

事件最早发生时间Ve[u]、事件最晚发生时间Vl[u]

活动最早发生时间e[r]、活动最晚发生时间l[r]

在AOE网(Activity On Edge,用带权的边表示活动,用顶点表示事件的有向图)中,【其实一个事件(顶点)仅表示一个中介状态】首先,我们要求出每个事件(顶点)的最早发生时间Ve[u]和最晚发生时间Vl[u],“事件最早发生时间”可用拓扑排序来进行求解;而事件的最晚发生时间可以用逆拓扑来求解。以上两个求解出来后,就可以计算活动最早发生时间e[r]和活动最晚发生时间l[r]了,他们的关系如下:

e[r]=Ve[u]

l[r]+length[r]=Vl[v]

代码如下:


  
  
  1. #include"stdafx.h"
  2. #include<cstdio>
  3. #include<stack>
  4. #include<algorithm>
  5. #include<cmath>
  6. #include<iostream>
  7. #include<vector>
  8. #include<queue>
  9. using namespace std;
  10. const int maxn = 500;
  11. int Vertexnum, Edgenum;
  12. struct node {
  13. int v;
  14. int weight;
  15. };
  16. vector<node> adj[maxn]; //邻接表存储DAG图
  17. int innode[maxn] = { 0 }; //记录每个结点的入度
  18. queue< int> q;
  19. stack< int> s; //运用一个栈,到时候实现逆拓扑
  20. int Ve[maxn]; //事件发生的最早时间
  21. int Vl[maxn]; //事件发生的最晚时间
  22. bool topologicalsort() { //拓扑排序
  23. for ( int i = 0; i < Vertexnum; i++) { //将所有入度为0的点加入到队列中
  24. if (innode[i]== 0) {
  25. q.push(i);
  26. }
  27. }
  28. while (!q.empty()) {
  29. int u = q.front(); //取队首元素
  30. q.pop();
  31. s.push(u); //进入栈
  32. for ( int i = 0; i < adj[u].size(); i++) { //进行入度更新操作
  33. int v = adj[u][i].v;
  34. innode[v]--;
  35. if (innode[v] == 0) { //如果入度为0的点,则入队
  36. q.push(v);
  37. }
  38. if (Ve[u] + adj[u][i].weight > Ve[v]) { //计算事件最早发生时间
  39. Ve[v] = Ve[u] + adj[u][i].weight;
  40. }
  41. }
  42. }
  43. if (s.size() == Vertexnum) return true;
  44. else return false; //否则拓扑失败,说明此图是存在环,不是DAG图
  45. }
  46. void antitopologicalsort() { //逆拓扑求事件最晚发生时间
  47. while (!s.empty()) {
  48. int u = s.top();
  49. s.pop();
  50. for ( int i = 0; i < adj[u].size(); i++) { //计算事件最晚发生时间
  51. int v = adj[u][i].v;
  52. if (Vl[v] - adj[u][i].weight<Vl[u]) {
  53. Vl[u] = Vl[v] - adj[u][i].weight;
  54. }
  55. }
  56. }
  57. }
  58. void criticalpath() { //关键路径
  59. fill(Ve, Ve + maxn, 0); //事件最早发生时间初始化
  60. if (topologicalsort() == false) return; //拓扑失败
  61. fill(Vl, Vl + maxn, Ve[Vertexnum - 1]); //事件最晚发生时间初始化
  62. antitopologicalsort();
  63. cout << "关键路径长度:" << Ve[Vertexnum - 1] << "\n"; //输出关键路径长度
  64. for ( int u = 0; u < Vertexnum; u++) { //遍历所有的边
  65. for ( int i = 0; i < adj[u].size(); i++) {
  66. int v = adj[u][i].v; //边的两个端点分别是u和v
  67. int e = Ve[u]; //活动最早发生时间
  68. int l = Vl[v] - adj[u][i].weight; //活动最晚发生时间
  69. if (e == l) {
  70. cout << u << "->" << v << "\n"; //输出关键路径
  71. }
  72. }
  73. }
  74. }
  75. int main() {
  76. cin >> Vertexnum >> Edgenum;
  77. int start, end, weight;
  78. for ( int i = 0; i < Edgenum; i++) {
  79. cin >> start >> end >> weight;
  80. node N;
  81. N.v = end;
  82. N.weight = weight;
  83. innode[end]++; //入度加1
  84. adj[start].push_back(N);
  85. }
  86. criticalpath(); //关键路径
  87. return 0;
  88. }

运行结果如下:



    补充:其实求AOE网中的关键路径就是求DAG最长路路径,所以对于以上求DAG最长路还有一种更简单的办法:利用动态规划思想去解决,令dp[i]表示以i为源点的最长路,如果要求dp[i],就必须求出以源点i出发能够到达的顶点j的dp[j],而dp[j]的求解又是一样的思路,其中DAG图中出度为0的点v,他的dp[v]就是为0了,很显然,求解dp数组可以利用递归去求解。当求出所有的dp[i],其中dp值最大的就是DAG图的最长路径长度,代码如下:

以下算法实现的功能是:求出最长路径长度和输出最长路径序列


  
  
  1. #include"stdafx.h"
  2. #include<cstdio>
  3. #include<stack>
  4. #include<algorithm>
  5. #include<cmath>
  6. #include<iostream>
  7. #include<vector>
  8. #include<queue>
  9. using namespace std;
  10. const int maxn = 500;
  11. int Vertexnum, Edgenum;
  12. struct node {
  13. int v;
  14. int weight;
  15. };
  16. vector<node> adj[maxn]; //邻接表存储DAG图
  17. int outnode[maxn] = { 0 }; //记录每个顶点的出度
  18. int dp[maxn];
  19. void init() { //dp数组初始化
  20. fill(dp, dp + maxn, -1);
  21. for ( int i = 0; i < Vertexnum; i++) {
  22. if (outnode[i] == 0) { //初始化出度为0的dp为0
  23. dp[i] = 0;
  24. }
  25. }
  26. }
  27. vector< int> sumpath[maxn]; //sumpath[i]数组表示:以i为源点的DAG最长路路径序列
  28. int path[maxn]; //存放DAG最长路路径序列
  29. int DFS(int index) { //递归求解dp数组
  30. if (dp[index] == 0) return dp[index]; //到达递归边界
  31. for ( int i = 0; i < adj[index].size(); i++) {
  32. int v = adj[index][i].v;
  33. int weight = adj[index][i].weight;
  34. int distv = DFS(v);
  35. if (dp[index] < distv + weight) {
  36. dp[index] = distv + weight;
  37. path[index] = v; //顶点index的后继结点是v(和Dijkstra算法记录前驱结点的思路差不多)
  38. }
  39. }
  40. return dp[index];
  41. }
  42. int main() {
  43. cin >> Vertexnum >> Edgenum;
  44. int start, end, weight;
  45. for ( int i = 0; i < Edgenum; i++) {
  46. cin >> start >> end >> weight;
  47. node N;
  48. N.v = end;
  49. N.weight = weight;
  50. outnode[start]++; //更新结点的出度
  51. adj[start].push_back(N);
  52. }
  53. init(); //初始化
  54. for ( int i = 0; i < Vertexnum; i++) {
  55. path[i] = i; //初始化每个结点的后继结点为自身
  56. }
  57. int maxdp = -1; //记录最大的dp[i]的值
  58. int longestindex; //表示DAG最长路路径是以longestindex为源点
  59. for ( int i = 0; i < Vertexnum; i++) {
  60. int temp = DFS(i);
  61. sumpath[i].push_back(i);
  62. int j = i;
  63. while (path[j] != j) {
  64. j = path[j];
  65. sumpath[i].push_back(j);
  66. }
  67. if (maxdp < temp) {
  68. maxdp = temp;
  69. longestindex = i;
  70. }
  71. }
  72. cout << "关键路径长度:" << maxdp << "\n";
  73. cout << "关键路径序列为:";
  74. for ( int i = 0; i < sumpath[longestindex].size(); i++) { //输出以longestindex为源点的DAG最长路
  75. cout << sumpath[longestindex][i];
  76. }
  77. return 0;
  78. }

运行结果如下:



猜你喜欢

转载自blog.csdn.net/qq_39685968/article/details/104538161