这一章看了好长时间,花了一个下午才搞懂,其中关键路径还是挺有意思的。一开始我是想先拓扑排序,然后按顺序求关键路径,但是用贪心是明显不行的,后来想用动态规划,但是憋了十几分钟,没憋出来,最后参考了一下网上其他人的写法,总结出来这篇。
#pragma once //杂注,使头文件只编译一次
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<cstdlib>
#include<cmath>
#include<stack>
#include<algorithm>
#define INF 0xfffff
#define maxn 10000
using namespace std;
int Ve[maxn]; //存储事件的最早发生时间
int Vl[maxn]; //存储事件的最迟发生时间
int indegree[10000]; //建立一个入度数组,入度为零,证明就可以进栈了
//用邻接表建立有向无圈图,先进行拓扑排序,然后求关键路径;
//邻接表的结构体
typedef struct ArcNode { //弧结构体
int adjvex; //邻接点域,存放与Vi邻接的点在表头数组中的位置
struct ArcNode *nextarc; //链域,指示下一条边或弧
//InfoType *info; //存储与边或弧相关的信息,如权值
int info;
} ArcNode;
typedef struct VNode{ //顶点结构体
char data; //存放顶点信息
ArcNode *firstarc; //指示链表中第一个结点
//int indegree; //表示一共有多少个点指向这个节点
} VNode;
typedef struct{
VNode vex[maxn]; // 顶点表列(邻接表)
int vex_num, arc_num; // 图的当前顶点数和弧数
} Graph;
int LocateVex(Graph G, char v) {
for(int i=0;i<G.vex_num;i++) {
if(v == G.vex[i].data)
return i;
}
return -1;
}
void CreateGraph(Graph &G) {
ArcNode *p, *s;
char v1, v2;
int power; //各条边的权值
cout<<"请输入节点数和边数目"<<endl;
cin>>G.vex_num>>G.arc_num;
getchar(); //因为定义的data是 char 类型的,所以这里必须要吃掉一个回车
cout<<"请输入节点"<<endl;
for(int i=0;i<G.vex_num;i++) {
cin>>G.vex[i].data;
G.vex[i].firstarc = NULL;
}
cout<<"输入各条边和他们的权值:"<<endl;
for(int i=0;i<G.arc_num;i++) {
getchar(); //char 类型的,每次都要吃掉一个回车
scanf("%c %c %d", &v1, &v2, &power);
int j = LocateVex(G, v1);
int k = LocateVex(G, v2);
s = (ArcNode *) malloc (sizeof(ArcNode));
s->adjvex = k;
s->info = power;
s->nextarc = NULL;
if(!G.vex[j].firstarc) //将v2 直接接到列的顶点的上面;
G.vex[j].firstarc = s;
else {
p = G.vex[j].firstarc; //如果这个数据不是第一个接到列顶点上面的,那么就顺次向后移
while(p->nextarc) //直到移到最后
p = p->nextarc;
p->nextarc = s;
}
}
}
void FindInputDgeree(Graph G){ //计算所有节点的入度
ArcNode *p;
for(int i=0;i<G.vex_num;i++)
indegree[i] = 0;
for(int i=0;i<G.vex_num;i++) {
p = G.vex[i].firstarc;
while(p) {
indegree[p->adjvex] ++;
p = p->nextarc;
}
}
}
void Topology(Graph G) {
stack<int> s;
FindInputDgeree(G);
int count=0, i;
ArcNode *p;
cout<<"拓扑排序序列如下:"<<endl;
for(i=0;i<G.vex_num;i++)
if(!indegree[i])
s.push(i); //把入度为零的节点入栈
while(!s.empty()){
i = s.top(); //获得栈顶
s.pop(); //弹出栈;
cout<<G.vex[i].data<<endl;
count++;
p = G.vex[i].firstarc;
while(p) {
if(! (--indegree[p->adjvex]) )//判断去掉一条边后节点的入度是否为零
s.push(p->adjvex);
p = p->nextarc;
}
}
if(count < G.vex_num)
cout<<"这是一个有圈的图"<<endl;
cout<<endl;
}
void CriticalPath(Graph G) {
int i, k, e, l;
ArcNode * p;
//以下是求时间最早发生时间
for(i = 0; i < G.vex_num; i++)
Ve[i] = 0; //最早发生时间全部初始化为0
for(i = 0; i < G.vex_num; i++) {
ArcNode *p = G.vex[i].firstarc;
while(p != NULL) {
k = p->adjvex;
if(Ve[i] + p->info > Ve[k]) //动态求得当前最长,也就是最早时间
Ve[k] = Ve[i] + p->info;
p = p->nextarc;
}
}
//以下是求最迟发生时间
for(i = 0; i < G.vex_num; i++)
Vl[i] = Ve[G.vex_num-1]; //对最迟发生时间进行初始化
for(i = G.vex_num-2; i >= 0; i--) { //后推
p = G.vex[i].firstarc;
while(p != NULL) {
k = p->adjvex;
if(Vl[k] - p->info < Vl[i])
Vl[i] = Vl[k] - p->info;
p = p->nextarc;
}
}
for(i = 0; i < G.vex_num; i++) {
p = G.vex[i].firstarc;
while(p != NULL) {
k = p->adjvex;
e = Ve[i]; //最早开始时间为时间vi的最早发生时间
l = Vl[k] - p->info; //最迟开始时间
//char tag = (e == l) ? '*' : ' '; //关键活动就是没有缓冲时间,即最早开始时间和最长开始时间相等
if(e == l)
printf("(%c, %c), e = %2d, l = %2d\n", G.vex[i].data, G.vex[k].data, e, l);
p = p->nextarc;
}
}
}
int main() {
Graph G;
CreateGraph(G); //创建邻接表
Topology(G); //进行拓扑排序
CriticalPath(G);
return 0;
}
//进行了拓扑排序
//带权值的拓扑排序就是求关键路径
测试数据:
6 8
123456
1 2 3
1 3 2
2 4 2
2 5 3
3 4 4
3 6 3
4 6 2
5 6 1