拓扑排序之关键路径(深度优先搜索)

/*

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 10

0 1 5

0 2 4

1 4 3

2 3 2

3 4 1

4 5 6

4 6 6

5 7 2

6 7 2

4 7 8

输出:

输出所有关键路径,每行输出一个关键路径。格式如下:

0->1->4->7

0->1->4->5->7

0->1->4->6->7

算法分析:

采用深度优先搜索进行拓扑排序,获取拓扑序列的同时计算各顶点事件的最早发生时间,然后逆序计算各顶点事件的最晚发生时间。

本文是《大话数据结构》的读书笔记,在输出关键路径时采用深度优先搜索输出关键路径,能输出多条关键路径。

*/

#include<stdio.h>

#include<stdlib.h>

#define MAXN 26   //最大顶点数量

#define MAXM 100000   //最大边数量

typedef int 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 CreateGraph(VertexNode *GL, int n, int m);//把顶点和边信息读入到表示图的邻接表中

int TopoLogicalSort_DFS(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;

VertexNode GL[MAXN];

printf("请输入顶点数量和边数量:");

scanf("%d%d", &n, &m);

CreateGraph(GL, n, m);//把顶点和边信息读入到表示图的邻接表中

CriticalPath(GL, n);//求关键路径

return 0;

}

void CreateGraph(VertexNode *GL, int n, int m)//把顶点和边信息读入到表示图的邻接表中

{

int i, u, v;

EdgeNode *e;

for (i=0; i<n; i++)//初始化图

{

GL[i].data = i;

GL[i].in = 0;

GL[i].firstEdge = NULL;

}

for (i=0; i<m; i++)

{

e = (EdgeNode*)malloc(sizeof(EdgeNode)); //采用头插法插入边表结点

if (!e)

{

puts("Error");

exit(1);

}

scanf("%d%d%d", &u, &v, &e->weight);

e->next = GL[u].firstEdge;

GL[u].firstEdge = e;

e->adjvex = v;

GL[v].in++;

}

}

int TopoLogicalSort_DFS(int topo[], int Etv[], VertexNode *GL, int n)//深度优先搜索获取拓扑序列

{

int i, u, v, top, count = 0;

EdgeNode *e;

int *Stack;

Stack = (int*)malloc(sizeof(int) * n); //分配栈空间

if (!Stack)

{

puts("Error");

exit(1);

}

for (top=i=0; i<n; i++)//将入度为0的顶点入栈

{

Etv[i] = 0; //初始化各事件最早发生事件为0

if (GL[i].in == 0)

{

Stack[top++] = i;

}

}

while (top > 0)//采用深度优先搜索获取拓扑序列

{

u = Stack[--top];

topo[count++] = u;

for (e=GL[u].firstEdge; e!=NULL; e=e->next)//将u的邻接点入度减1,并将入度为0的顶点入栈

{

v = e->adjvex;

if (--GL[v].in == 0)

Stack[top++] = v;

if (Etv[v] < Etv[u] + e->weight)//更新各顶点事件的最早发生时间

Etv[v] = Etv[u] + e->weight;

}

}

free(Stack);

return (count == n);//如果count小于顶点数,说明存在环

}

void CriticalPath(VertexNode *GL, int n)//求关键路径

{

int i, u, v;

EdgeNode *e;

int topo[MAXN], path[MAXN];

int Etv[MAXN], Ltv[MAXN];//存储事件的最早和最晚发生时间

if (!TopoLogicalSort_DFS(topo, Etv, GL, n))

{

puts("不存在关键路径");

return;

}

for (i=0; i<n; i++)

{

Ltv[i] = Etv[n-1]; //初始化各事件最晚发生事件为最后一个事件发生的时间

}

for (i=n-2; i>=0; i--)

{

u = topo[i];

for (e=GL[u].firstEdge; e!=NULL; e=e->next)

{

v = e->adjvex;

if (Ltv[u] > Ltv[v] - e->weight)//更新各顶点事件的最晚发生时间

Ltv[u] = Ltv[v] - 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");

return;

}

for (e=GL[u].firstEdge; e!=NULL; e=e->next)

{

if (Etv[e->adjvex] == Ltv[e->adjvex])//关键事件

{

path[top++] = e->adjvex; //入栈

PrintPath(GL, Etv, Ltv, path, top, end);

top--; //退栈

}

}

}

时间: 2024-11-07 02:50:15

拓扑排序之关键路径(深度优先搜索)的相关文章

hdu 5098 Smart Software Installer 拓扑排序or记忆化搜索

Smart Software Installer Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 416    Accepted Submission(s): 124 Problem Description The software installation is becoming more and more complex. An a

图的拓扑排序、关键路径、最短路径算法 -- C++实现

一:拓扑排序 对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前.通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列. 拓扑排序就是要找到入度为0的顶点,并依次压栈.首先将栈顶顶点输出,删除以此顶点为弧尾的顶点,并更新其他顶点的入度.若入度为0,则继续压栈.更新完毕继续出栈,直至栈空.元素出栈并输出

[从今天开始修炼数据结构]无环图的应用 —— 拓扑排序和关键路径算法

上一篇文章我们学习了最短路径的两个算法.它们是有环图的应用.下面我们来谈谈无环图的应用. 一.拓扑排序 博主大学学的是土木工程,在老本行,施工时很关键的节约人力时间成本的一项就是流水施工,钢筋没绑完,浇筑水泥的那帮兄弟就得在那等着,所以安排好流水施工,让工作周期能很好地衔接就很关键.这样的工程活动,抽象成图的话是无环的有向图. 在表示工程的有向图中,用顶点表示活动,弧表示活动之间的优先关系,这样的有向图为顶点表示活动的网,成为AOV网(Active On Vertex Network) ※ 若在

拓扑排序之变量序列算法分析

拓扑排序之变量序列 巧若拙(欢迎转载,但请注明出处:http://blog.csdn.net/qiaoruozhuo) 题目描述: 假设有n个变量(1<=n<=26,变量名用单个小写字母表示),还有m个二元组(u,v),分别表示变量u小于v.那么,所有变量从小到大排列起来应该是什么样子的呢? 例如有4个变量a,b,c,d,若以知a<b,c<b,d<c,则这4个变量的排序可能是a<d<c<b.尽管还有可能其他的可能,你只需找出其中的一个即可. 输入: 输入为一

拓扑排序之变量序列代码

/* Name: Copyright: Author: Date: 17-11-14 21:02 Description: 拓扑排序之变量序列 假设有n个变量(1<=n<=26,变量名用单个小写字母表示),还有m个二元组(u,v),分别表示变量u小于v.那么,所有变量从小到大排列起来应该是什么样子的呢? 例如有4个变量a,b,c,d,若以知a<b,c<b,d<c,则这4个变量的排序可能是a<d<c<b.尽管还有可能其他的可能,你只需找出其中的一个即可. In

图的深度优先搜索及拓扑排序

本文将介绍图的深度优先搜索,并实现基于深度优先搜索的拓扑排序(拓扑排序适用于有向无环图,下面详细介绍). 1. 图的深度优先遍历要解决的问题 图的深度优先搜索与树的深度优先搜索类似,但是对图进行深度优先搜索要解决一个问题,那就是顶点的重复访问,假设图中存在一个环路A-B-C-A,那么对顶点A进行展开后得到B,对B进行展开后得到C,然后对C进行展开后得到A,然后A就被重复访问了... 这显然是不对的!我们需要用一个状态变量来记录一个顶点被访问和被展开的状态.在<算法导论>中,作者使用3种颜色来对

图论 邻接链表存储 BFS DFS 拓扑排序

package Algorithms; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.Stack; public class Graphic { public static class Vertex{ public int num;//节点编号 public int weight;//边的权重 public Vertex next;//指向顶点的

[算法小练][图][拓扑排序+深度优先搜索] 平板涂色问题

说在前面 本题是一道经典题目,多做经典题目可以节省很多学习时间,比如本题就包含了许多知识:回溯+剪枝+拓扑排序+深度优先搜索.[动态规划方法另作讨论] 关键代码 题: CE数码公司开发了一种名为自动涂色机(APM)的产品.它能用预定的颜色给一块由不同尺寸且互不覆盖的矩形构成的平板涂色. 为了涂色,APM需要使用一组刷子.每个刷子涂一种不同的颜色C.APM拿起一把有颜色C的刷子,并给所有颜色为C且符合下面限制的矩形涂色: 为了避免颜料渗漏使颜色混合,一个矩形只能在所有紧靠它上方的矩形涂色后,才能涂

算法系列笔记6(有关图的算法一—搜索,拓扑排序和强连通分支)

简单概念:对于图G(V,E),通常有两种存储的数据结构,一种是邻接矩阵,此时所需要的存储空间为O(V^2):第二种是邻接表,所需要的存储空间为O(V+E).邻接表表示法存在很强的适应性,但是也有潜在的不足,当要快速的确定图中边(u,v)是否存在,只能在顶点u的邻接表中搜索v,没有更快的方法,此时就可以使用邻接矩阵,但要以占用更多的存储空间作为代价:此外当图不是加权的,采用邻接矩阵存储还有一个优势:在存储邻接矩阵的每个元素时,可以只用一个二进位,而不必用一个字的空间. 图的搜索算法 搜索一个图示有