C++ AOE网络
一、思路(你可以用拓撲排序來做,但我這里沒用拓撲排序)
??? (1)求事件Vi的最早可能開始時間是從源點V0到頂點Vi的最長路徑長度。
???????????? 如V0=0, V1=6,V2=4,V3=5;V4事件要等V1和V2事件完成后才可以進行,所以要取事件用的最長的時間,即V4=6+1=7,同樣道理得出其他時間。
??? (2)事件Vi的最遲允許開始時間Vl[i]是在保證匯點Vn-1在Ve[n-1]時刻完成的前提下,事件Vi的允許的最遲開始時間。它等于Ve[n-1]減去從Vi到Vn-1的最長路徑。
???????? 如:事件V2=Ve[n-1]-(a7+a10)-a5 = 18-11-1=6
??? (3)活動ak的最早可能開始時間Ae[k]=Ve[i](k=1,2,…)(i=0,1,…)
??? (4)活動ak的最遲允許開始時間Al[k]=Vl[j]-<i,j>
??? (5)用Al[k]-Ae[k]表示活動ak的最早可能開始時間和最遲允許開始時間的時間余量,也叫做松弛時間。Al[k]==Ae[k]表示活動ak是沒有時間余量的關鍵活動。
| 事件 | V0 | V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 |
| Ve[i] | 0 | 6 | 4 | 5 | 7 | 7 | 16 | 14 | 18 |
| Vl[i] | 0 | 6 | 6 | Ve[8]-a11-a9-a6=8 | 7 | 10 | 16 | 14 | 18 |
?
| 邊 | <0, 1> | <0, 2> | <0, 3> | <1, 4> | <2, 4> | <3, 5> | <4, 6> | <4, 7> | <5, 7> | <6, 8> | <7, 8> |
| 活動 | a1 | a2 | a3 | a4 | a5 | a6 | a7 | a8 | a9 | a10 | a11 |
| Ae | Ve[i]=Ve[0]=0 | Ve[i]=Ve[0]=0 | Ve[i]=Ve[0]=0 | Ve[i]=Ve[1]=6 | Ve[2]=0 4 | 5 | 7 | 7 | 7 | 16 | 14 |
| Al | Vl[1]-6=6-6=0 | Vl[2]-<0,2>=6-4=2 | Vl[3]-<0,3>=8-5=3 | 6 | 6 | 8 | 7 | 7 | 10 | 16 | 14 |
| Al-Ae | 0 | 2 | 3 | 0 | 2 | 3 | 0 | 0 | 3 | 0 | 0 |
| 關鍵活動 | 是 | ? | ? | 是 | ? | ? | 是 | 是 | ? | ? | 是 |
?
二、實現程序:
1.Graph.h:有向圖的鏈表表示
#ifndef Graph_h #define Graph_h#include <iostream> using namespace std;const int DefaultVertices = 30;template <class T, class E> struct Edge { // 邊結點的定義int dest; // 邊的另一頂點位置E cost; // 表上的權值Edge<T, E> *link; // 下一條邊鏈指針 };template <class T, class E> struct Vertex { // 頂點的定義T data; // 頂點的名字Edge<T, E> *adj; // 邊鏈表的頭指針 };template <class T, class E> class Graphlnk { public:const E maxValue = 100000; // 代表無窮大的值(=∞)Graphlnk(int sz=DefaultVertices); // 構造函數~Graphlnk(); // 析構函數void inputGraph(); // 建立鄰接表表示的圖void outputGraph(); // 輸出圖中的所有頂點和邊信息T getValue(int i); // 取位置為i的頂點中的值E getWeight(int v1, int v2); // 返回邊(v1, v2)上的權值bool insertVertex(const T& vertex); // 插入頂點bool insertEdge(int v1, int v2, E weight); // 插入邊bool removeVertex(int v); // 刪除頂點bool removeEdge(int v1, int v2); // 刪除邊int getFirstNeighbor(int v); // 取頂點v的第一個鄰接頂點int getNextNeighbor(int v,int w); // 取頂點v的鄰接頂點w的下一鄰接頂點int getVertexPos(const T vertex); // 給出頂點vertex在圖中的位置int numberOfVertices(); // 當前頂點數 private:int maxVertices; // 圖中最大的頂點數int numEdges; // 當前邊數int numVertices; // 當前頂點數Vertex<T, E> * nodeTable; // 頂點表(各邊鏈表的頭結點) };// 構造函數:建立一個空的鄰接表 template <class T, class E> Graphlnk<T, E>::Graphlnk(int sz) {maxVertices = sz;numVertices = 0;numEdges = 0;nodeTable = new Vertex<T, E>[maxVertices]; // 創建頂點表數組if(nodeTable == NULL) {cerr << "存儲空間分配錯誤!" << endl;exit(1);}for(int i = 0; i < maxVertices; i++)nodeTable[i].adj = NULL; }// 析構函數 template <class T, class E> Graphlnk<T, E>::~Graphlnk() {// 刪除各邊鏈表中的結點for(int i = 0; i < numVertices; i++) {Edge<T, E> *p = nodeTable[i].adj; // 找到其對應鏈表的首結點while(p != NULL) { // 不斷地刪除第一個結點nodeTable[i].adj = p->link;delete p;p = nodeTable[i].adj;}}delete []nodeTable; // 刪除頂點表數組 }// 建立鄰接表表示的圖 template <class T, class E> void Graphlnk<T, E>::inputGraph() {int n, m; // 存儲頂點樹和邊數int i, j, k;T e1, e2; // 頂點E weight; // 邊的權值cout << "請輸入頂點數和邊數:" << endl;cin >> n >> m;cout << "請輸入各頂點:" << endl;for(i = 0; i < n; i++) {cin >> e1;insertVertex(e1); // 插入頂點}cout << "請輸入圖的各邊的信息:" << endl;i = 0;while(i < m) {cin >> e1 >> e2 >> weight;j = getVertexPos(e1);k = getVertexPos(e2);if(j == -1 || k == -1)cout << "邊兩端點信息有誤,請重新輸入!" << endl;else {insertEdge(j, k, weight); // 插入邊i++;}} // while }// 輸出有向圖中的所有頂點和邊信息 template <class T, class E> void Graphlnk<T, E>::outputGraph() {int n, m, i;T e1, e2; // 頂點E weight; // 權值Edge<T, E> *p;n = numVertices;m = numEdges;cout << "圖中的頂點數為" << n << ",邊數為" << m << endl;for(i = 0; i < n; i++) {p = nodeTable[i].adj;while(p != NULL) {e1 = getValue(i); // 有向邊<i, p->dest>e2 = getValue(p->dest);weight = p->cost;cout << "<" << e1 << ", " << e2 << ", " << weight << ">" << endl;p = p->link; // 指向下一個鄰接頂點}} }// 取位置為i的頂點中的值 template <class T, class E> T Graphlnk<T, E>::getValue(int i) {if(i >= 0 && i < numVertices)return nodeTable[i].data;return NULL; }// 返回邊(v1, v2)上的權值 template <class T, class E> E Graphlnk<T, E>::getWeight(int v1, int v2) {if(v1 != -1 && v2 != -1) {Edge<T , E> *p = nodeTable[v1].adj; // v1的第一條關聯的邊while(p != NULL && p->dest != v2) { // 尋找鄰接頂點v2p = p->link;}if(p != NULL)return p->cost;}return maxValue; // 邊(v1, v2)不存在,就存放無窮大的值 }// 插入頂點 template <class T, class E> bool Graphlnk<T, E>::insertVertex(const T& vertex) {if(numVertices == maxVertices) // 頂點表滿,不能插入return false;nodeTable[numVertices].data = vertex; // 插入在表的最后numVertices++;return true; }// 插入邊 template <class T, class E> bool Graphlnk<T, E>::insertEdge(int v1, int v2, E weight) {if(v1 >= 0 && v1 < numVertices && v2 >= 0 && v2 < numVertices) {Edge<T, E> *p = nodeTable[v1].adj; // v1對應的邊鏈表頭指針while(p != NULL && p->dest != v2) // 尋找鄰接頂點v2p = p->link;if(p != NULL) // 已存在該邊,不插入return false;p = new Edge<T, E>; // 創建新結點p->dest = v2;p->cost = weight;p->link = nodeTable[v1].adj; // 鏈入v1邊鏈表nodeTable[v1].adj = p;numEdges++;return true;}return false; }// 有向圖刪除頂點較麻煩 template <class T, class E> bool Graphlnk<T, E>::removeVertex(int v) {if(numVertices == 1 || v < 0 || v > numVertices)return false; // 表空或頂點號超出范圍Edge<T, E> *p, *s;// 1.清除頂點v的邊鏈表結點w 邊<v,w>while(nodeTable[v].adj != NULL) {p = nodeTable[v].adj;nodeTable[v].adj = p->link;delete p;numEdges--; // 與頂點v相關聯的邊數減1} // while結束// 2.清除<w, v>,與v有關的邊for(int i = 0; i < numVertices; i++) {if(i != v) { // 不是當前頂點vs = NULL;p = nodeTable[i].adj;while(p != NULL && p->dest != v) {// 在頂點i的鏈表中找v的頂點s = p;p = p->link; // 往后找}if(p != NULL) { // 找到了v的結點if(s == NULL) { // 說明p是nodeTable[i].adjnodeTable[i].adj = p->link;} else {s->link = p->link; // 保存p的下一個頂點信息}delete p; // 刪除結點pnumEdges--; // 與頂點v相關聯的邊數減1}}}numVertices--; // 圖的頂點個數減1nodeTable[v].data = nodeTable[numVertices].data; // 填補,此時numVertices,比原來numVertices小1,所以,這里不需要numVertices-1nodeTable[v].adj = nodeTable[numVertices].adj;// 3.要將填補的頂點對應的位置改寫for(int i = 0; i < numVertices; i++) {p = nodeTable[i].adj;while(p != NULL && p->dest != numVertices) // 在頂點i的鏈表中找numVertices的頂點p = p->link; // 往后找if(p != NULL) // 找到了numVertices的結點p->dest = v; // 將鄰接頂點numVertices改成v}return true; }// 刪除邊 template <class T, class E> bool Graphlnk<T, E>::removeEdge(int v1, int v2) {if(v1 != -1 && v2 != -1) {Edge<T, E> * p = nodeTable[v1].adj, *q = NULL;while(p != NULL && p->dest != v2) { // v1對應邊鏈表中找被刪除邊q = p;p = p->link;}if(p != NULL) { // 找到被刪除邊結點if(q == NULL) // 刪除的結點是邊鏈表的首結點nodeTable[v1].adj = p->link;elseq->link = p->link; // 不是,重新鏈接delete p;return true;}}return false; // 沒有找到結點 }// 取頂點v的第一個鄰接頂點 template <class T, class E> int Graphlnk<T, E>::getFirstNeighbor(int v) {if(v != -1) {Edge<T, E> *p = nodeTable[v].adj; // 對應鏈表第一個邊結點if(p != NULL) // 存在,返回第一個鄰接頂點return p->dest;}return -1; // 第一個鄰接頂點不存在 }// 取頂點v的鄰接頂點w的下一鄰接頂點 template <class T, class E> int Graphlnk<T, E>::getNextNeighbor(int v,int w) {if(v != -1) {Edge<T, E> *p = nodeTable[v].adj; // 對應鏈表第一個邊結點while(p != NULL && p->dest != w) // 尋找鄰接頂點wp = p->link;if(p != NULL && p->link != NULL)return p->link->dest; // 返回下一個鄰接頂點}return -1; // 下一個鄰接頂點不存在 }// 給出頂點vertex在圖中的位置 template <class T, class E> int Graphlnk<T, E>::getVertexPos(const T vertex) {for(int i = 0; i < numVertices; i++)if(nodeTable[i].data == vertex)return i;return -1; }// 當前頂點數 template <class T, class E> int Graphlnk<T, E>::numberOfVertices() {return numVertices; }#endif /* Graph_h */2.AOE.h
#ifndef AOE_h #define AOE_h #include "Graph.h"// 計算關鍵路徑的算法 template <class T, class E> void CirticalPath(Graphlnk<T, E>& G) {int i, j, k, n;E Ae, Al, w;n = G.numberOfVertices(); // 圖的頂點數E *Ve = new E[n];E *Vl = new E[n];for(i = 0; i < n; i++) // 初始化最早開始事件時間Ve[i] = 0;// 正向計算Ve[i],時間Vi的最早可能開始時間:從源點V0到頂點Vi的最長路徑長度for(i = 0; i < n; i++) {j = G.getFirstNeighbor(i); // 鄰接頂點while(j != -1) { // 存在鄰接頂點w = G.getWeight(i, j);if(Ve[i] + w > Ve[j]) // 要等前面時間最長的活動完了,才可以進行j事件Ve[j] = Ve[i] + w;j = G.getNextNeighbor(i, j); // 下一個鄰接頂點}}// 逆向計算Vl[]Vl[n-1] = Ve[n-1]; // 最終完成事件時間for(j = n-2; j > 0; j--) {k = G.getFirstNeighbor(j);Vl[j] = Ve[n-1]; // 假設最大為Ve[n-1]while(k != -1) { // 存在鄰接邊w = G.getWeight(j, k);if(Vl[k] - w < Vl[j])Vl[j] = Vl[k] - w; // 事件最遲允許開始時間k = G.getNextNeighbor(j, k);}}for(i = 0; i < n; i++) { // 求各活動的Ae, A1j = G.getFirstNeighbor(i);while(j != -1) {Ae = Ve[i]; // 活動ak最早可能開始時間Al = Vl[j] - G.getWeight(i, j); // 時間j最遲可能開始時間-活動ak需要的時間if(Al == Ae)cout << "<" << G.getValue(i) << "," << G.getValue(j) << ">" << "是關鍵活動" << endl;j = G.getNextNeighbor(i, j);}}delete []Ve; // 釋放動態分配的空間delete []Vl; }#endif /* AOE_h */3.main.cpp
#include "AOE.h"int main(int argc, const char * argv[]) {Graphlnk<char, int> G; // 聲明圖對象// 創建圖G.inputGraph();cout << "圖的信息如下:" << endl;G.outputGraph();// 調用計算關鍵路徑的函數cout << "關鍵路徑如下:" << endl;CirticalPath(G);return 0; }測試數據:
9 11
0 1 2 3 4 5 6 7 8
0 1 6
0 2 4
0 3 5
1 4 1
2 4 1
3 5 2
4 6 9
4 7 7
5 7 4
6 8 2
7 8 4
?
測試結果:
?
總結
- 上一篇: Python图书商城(可运行代码)有说明
- 下一篇: 数据结构与算法 c++描述 目录与源码