使用广度优先搜索生成关键路径

来源:互联网 发布:python re.split 用法 编辑:程序博客网 时间:2024/06/05 23:47
/*    Name: 拓扑排序之关键路径     Copyright:     Author: 巧若拙     Date: 17-11-14 21:02    Description: 拓扑排序之关键路径     若在带权的有向图中,以顶点表示事件,以有向边表示活动,边上的权值表示活动的开销(如该活动持续时间),则此带权的有向图称为边表示活动的网 (Activity on Edge Network) ,简称 AOE 网。(1)AOV 网具有的性质 ⒈ 只有在某顶点所代表的事件发生后,从该顶点出发的各有向边所代表的活动才能开始。⒉ 只有在进入某一顶点的各有向边所代表的活动都已经结束,该顶点所代表的事件才能发生。 ⒊ 表示实际工程计划的 AOE 网应该是无环的,并且存在唯一的入度过为 0 的开始顶点和唯一的出度为 0 的完成顶点。(2)由事件 v j 的最早发生时间和最晚发生时间的定义 , 可以采取如下步骤求得关键活动 : 1. 从开始顶点 v 1 出发 , 令 ve(1)=0, 按拓朴有序序列求其余各顶点的可能最早发生时间。 Ve(k)=max{ve(j)+dut(<j,k>)} ( 7.1 ) j ∈ T 其中 T 是以顶点 v k 为尾的所有弧的头顶点的集合 (2 ≤ k ≤ n) 。 如果得到的拓朴有序序列中顶点的个数小于网中顶点个数 n ,则说明网中有环,不能求出关键路径,算法结束。 2. 从完成顶点 v n 出发,令 vl(n)=ve(n) ,按逆拓朴序列求其余各顶点的允许的最晚发生时间 : vl(j)=min{vl(k)-dut(<j,k>)} k ∈ S 其中 S 是以顶点 v j 是头的所有弧的尾顶点集合 (1 ≤ j ≤ n-1) 。 3. 求每一项活动 a i (1 ≤ i ≤ m) 的最早开始时间 e(i)=ve(j) ;最晚开始时间 l(i)=vl(k)-dut(<j,k>) 。若某条弧满足 e(i)=l(i) ,则它是关键活动。输入:第一行两个整数n,m分别表示顶点个数和边的条数,其中顶点的编号为0~n-1。接下来的m行,每行有三个数u,v,w,表示从弧尾u到弧头v的边的权值w。8 90 1 50 2 41 4 32 3 23 4 24 5 64 6 65 7 26 7 2输出:输出所有关键路径,每行输出一个关键路径。格式如下:0->2->3->4->5->70->2->3->4->6->70->1->4->5->70->1->4->6->7算法分析:采用广度优先搜索进行拓扑排序,获取拓扑序列的同时计算各顶点事件的最早发生时间,然后逆序计算各顶点事件的最晚发生时间。本文是《大话数据结构》的读书笔记,但算法实现与《大话数据结构》完全不同,自我感觉比书上的算法要简洁,哈哈! */#include<stdio.h>#include<stdlib.h>#define MAXN 26   //最大顶点数量 #define MAXM 100000   //最大边数量 typedef char VertexType; //顶点类型由用户自定义typedef int EdgeType; //边上的权值类型由用户自定义typedef struct EdgeNode{ //边表结点    int adjvex;  //邻接点域,存储该顶点对应的下标    EdgeType weight; //权值,对于非网图可以不需要    struct EdgeNode *next; //链域,指向下一个邻接点} EdgeNode;typedef struct VertexNode{ //顶点表结点    VertexType data; //顶点域,存储顶点信息int in;   //存储顶点入度的数量EdgeNode *firstEdge; //边表头指针} VertexNode;void PrintGraph(VertexNode GL[], int n);//输出图int TopoLogicalSort(int topo[], int Etv[], VertexNode GL[], int n);void CriticalPath(VertexNode GL[], int n);//求关键路径 void PrintPath(VertexNode GL[], int Etv[], int Ltv[], int path[], int top, int end);//深度优先搜索输出关键路径 int main(){    int i, m, n;    int u, v;    EdgeType w;    VertexNode GL[MAXN]; //存储顶点表结点信息    EdgeNode *e;        for (i=0; i<MAXN; i++)//初始化图     { GL[i].data = i;        GL[i].in = 0;        GL[i].firstEdge = NULL;    }        printf("请输入顶点数量和边数量:");     scanf("%d%d", &n, &m);        for (i=0; i<m; i++)    { scanf("%d%d%d", &u, &v, &w); e =(EdgeNode*)malloc(sizeof(EdgeNode)); //采用头插法插入边表结点if (!e){puts("Error");exit(1);}e->adjvex = v;e->weight = w;e->next = GL[u].firstEdge; GL[u].firstEdge = e;GL[v].in++;    }        PrintGraph(GL, n);    CriticalPath(GL, n);//求关键路径         system("pause");    return 0;}void PrintGraph(VertexNode GL[], int n)//输出图{    int i, j;    EdgeNode *e;        for (i=0; i<n; i++)    {        printf("G[%d] = %c: ", i, i+'a');        for(e=GL[i].firstEdge; e!=NULL; e=e->next)         {    printf("->%d,%d", e->adjvex, e->weight);        }          printf("\n");    }    printf("\n");} int TopoLogicalSort(int topo[], int Etv[], VertexNode GL[], int n){    int i, k, front, rear;    EdgeNode *e;        front = rear = 0;    for (i=0; i<n; i++)//将入度为0的顶点入队列     {        Etv[i] = 0; //初始化各事件最早发生事件为0         if (GL[i].in == 0)        {            topo[rear++] = i;        }    }        while (front < rear)//采用广度优先搜索获取拓扑序列     {        k = topo[front++];        for(e=GL[k].firstEdge; e!=NULL; e=e->next)//将u的邻接点入度减1,并将入度为0的顶点入栈        {        if (--GL[e->adjvex].in == 0)        {                topo[rear++] = e->adjvex;            }                        if (Etv[e->adjvex] < Etv[k] + e->weight)//更新各顶点事件的最早发生时间             {                Etv[e->adjvex] = Etv[k] + e->weight;            }        }      }        return (rear == n);//如果count小于顶点数,说明存在环 }void CriticalPath(VertexNode GL[], int n)//求关键路径 {    int i, j;    int topo[MAXN], path[MAXN];    int Etv[MAXN] = {0}, Ltv[MAXN];//存储事件的最早和最晚发生时间     EdgeNode *e;        if (!TopoLogicalSort(topo, Etv, GL, n))    {        puts("不存在关键路径");             return;    }        for (i=0; i<n; i++)    {        Ltv[i] = Etv[topo[n-1]]; //初始化各事件最晚发生事件为最后一个事件发生的时间     }        for (j=n-2; j>=0; j--)    { for(e=GL[topo[j]].firstEdge; e!=NULL; e=e->next)//将u的邻接点入度减1,并将入度为0的顶点入栈        {    if (Ltv[topo[j]] > Ltv[e->adjvex] - e->weight)//更新各顶点事件的最晚发生时间     {                Ltv[topo[j]] = Ltv[e->adjvex] - e->weight;}        }      }        path[0] = topo[0];    PrintPath(GL, Etv, Ltv, path, 1, topo[n-1]); }void PrintPath(VertexNode GL[], int Etv[], int Ltv[], int path[], int top, int end)//深度优先搜索输出关键路径 {int i, u = path[top-1];EdgeNode *e;if (u == end){printf("%d", path[0]); //输出关键路径 for (i=1; i<top; i++){printf("->%d", path[i]);}printf("\n");}else{ for(e=GL[path[top-1]].firstEdge; e!=NULL; e=e->next)        {if (Etv[e->adjvex] == Ltv[e->adjvex] && Ltv[e->adjvex] - e->weight == Ltv[u])//关键事件和关键路径 {path[top++] = e->adjvex; //入栈 PrintPath(GL, Etv, Ltv, path, top, end); top--; //退栈 }         }  }}