先说思路,答案是第一行代码,第二段代码是思考,题目在最后;
思路:先是正常分别用两个for循环分别求出每个结点的最早开始时间和最晚开始时间,而后创建一个栈,并把topo[0]---也就是起始点压入栈,而后类似深度优先搜索,只要栈不为空,每次取出栈顶元素v,并且用p遍历一遍v在图中指向的结点,如果该结点最晚开始时间==最早开始时间就把这个结点压入栈(用队列也可以实现输出路径,不过输出顺序不一样,用队列类似广度优先搜索,这道题对输出顺序有要求,我把队列实现放在第二段代码,可以了解一下)
答案代码:
int CriticalPath(ALGraph G){//栈实现,对多条关键路径深度优先输出
TopologicalOrder(G,topo);
int i,v,flag=1;
struct ArcNode* p;
ve[topo[0]]=0;vl[topo[0]]=0;
for(i=0;i<G.vexnum;i++){//最早开始时间 正推
v=topo[i];
p=G.vertices[v].firstarc;
while(p){
if(ve[p->adjvex]<ve[v]+p->weight) ve[p->adjvex]=ve[v]+p->weight;
p=p->nextarc;
}
}
vl[topo[G.vexnum-1]]=ve[topo[G.vexnum-1]];//最后一个结点的最早开始时间等于最晚开始时间
for(i=G.vexnum-1;i>=0;i--){//最晚开始时间 反推
v=topo[i];
p=G.converse_vertices[v].firstarc;
while(p){
if(vl[p->adjvex]==0||(vl[p->adjvex]>vl[v]-p->weight)) vl[p->adjvex]=vl[v]-p->weight;
p=p->nextarc;
}
}
//应该用栈实现,先把最前1压入栈,不用collected ,DFS的味道
int stack[G.vexnum],top=-1;
stack[++top]=topo[0];
while(top!=-1){
v=stack[top--];
p=G.vertices[v].firstarc;
while(p){
if(vl[p->adjvex]==ve[p->adjvex]){
if(flag) flag=0;
else printf(",");
printf("%c->%c",G.vertices[v].data,G.vertices[p->adjvex].data);
stack[++top]=p->adjvex;
}
p=p->nextarc;
}
}
return 1;
}
队列实现:(这个原理对,但不是这道题的正确答案,因为顺序错了)
int CriticalPath(ALGraph G){//用队列逻辑上也对,但是这道题对顺序有要求,用队列就是广度优先
TopologicalOrder(G,topo);
int i,v,flag=1;
struct ArcNode* p;
ve[topo[0]]=0;vl[topo[0]]=0;
for(i=0;i<G.vexnum;i++){//最早开始时间 正推
v=topo[i];
p=G.vertices[v].firstarc;
while(p){
if(ve[p->adjvex]<ve[v]+p->weight) ve[p->adjvex]=ve[v]+p->weight;
p=p->nextarc;
}
}
vl[topo[G.vexnum-1]]=ve[topo[G.vexnum-1]];//最后一个结点的最早开始时间等于最晚开始时间
for(i=G.vexnum-1;i>=0;i--){//最晚开始时间 反推
v=topo[i];
p=G.converse_vertices[v].firstarc;
while(p){
if(vl[p->adjvex]==0||(vl[p->adjvex]>vl[v]-p->weight)) vl[p->adjvex]=vl[v]-p->weight;
p=p->nextarc;
}
}
//应该用队列实现,先把最前1入队,不用collected ,有点BFS的味道
int queue[G.vexnum],f=-1,r=-1;
queue[++r]=topo[0];
while(f!=r){
v=queue[++f];
p=G.vertices[v].firstarc;
while(p){
if(vl[p->adjvex]==ve[p->adjvex]){
if(flag) flag=0;
else printf(",");
printf("%c->%c",G.vertices[v].data,G.vertices[p->adjvex].data);
queue[++r]=p->adjvex;
}
p=p->nextarc;
}
}
return 1;
}
题目:
试实现关键路径算法。函数int CriticalPath(ALGraph G)输出关键路径。
函数接口定义:
int CriticalPath(ALGraph G);
其中 G
是基于邻接表及逆邻接表存储表示的有向图。
裁判测试程序样例:
#include <iostream>
using namespace std;
#define MVNum 100
#define BDNum MVNum * (MVNum - 1)
#define OK 1
#define ERROR 0
typedef char VerTexType;
typedef struct ArcNode{
int adjvex;
int weight;
struct ArcNode *nextarc;
}ArcNode;
typedef struct VNode{
VerTexType data;
ArcNode *firstarc;
}VNode, AdjList[MVNum];
typedef struct{
AdjList vertices; //邻接表
AdjList converse_vertices;//逆邻接表
int vexnum, arcnum;
}ALGraph;
int indegree[MVNum];//数组indegree存放个顶点的入度
int ve[BDNum]; //事件vi的最早发生时间
int vl[BDNum]; //事件vi的最迟发生时间
int topo[MVNum]; //记录拓扑序列的顶点序号
int CreateUDG(ALGraph &G); //实现细节隐藏
void FindInDegree(ALGraph G,int indegree[]);//获取各个顶点的入度,indegree存放个顶点的入度,函数实现细节隐藏
int TopologicalOrder(ALGraph G , int topo[]);//拓扑排序,topo存放拓扑序列,函数实现细节隐藏
int CriticalPath(ALGraph G);
int main(){
ALGraph G;
CreateUDG(G);
int *topo = new int [G.vexnum];
CriticalPath(G);
return 0;
}
/* 请在这里填写答案 */
输入样例:
第1行输入结点数vexnum和边数arcnum。第2行输入vexnum个字符表示结点的值,接下来依次输入arcnum行,每行输入2个字符v和u,表示v到u有一条有向边。
9 11
1 2 3 4 5 6 7 8 9
1 2 6
1 3 4
1 4 5
2 5 1
3 5 1
4 6 2
5 7 9
5 8 7
6 8 4
7 9 2
8 9 4
输出样例:
输出关键路径。
1->2,2->5,5->8,5->7,7->9,8->9