已更完
github中有详尽代码可作参考
返回目录
最小生成树
对于一个带权连通无向图 G=(V,E),生成树不同,每棵树的权(即树中所有边上的权值之和)也可能不同,设 ℜ为 G的所有生成树的集合,若 T为 ℜ中边的权值之和最小的那棵生成树,则 T称为 G的最小生成树( MST).
最小生成树的性质:
- 最小生成树不是唯一的,即最小生成树的树形不唯一, ℜ中可能有多个最小生成树.当图 G中各边权值互不相同时, G的最小生成树是唯一的;若无向连通图 G的边数比顶点数少 1,即 G本身是一棵树时,则 G的最小生成树就是它本身.
- 最小生成树的边的权值之和总是唯一的,虽然最小生成树不唯一,但其对应的边的权值之和总是唯一的且是最小的.
- 最小生成树的边数为顶点数减 1.
Prim算法
该算法的时间复杂度为 O(|V|2),不依赖于 |E|,它可通过使用优先队列把时间复杂度优化为 O(|E|log2|V|),它适用于求解边稠密的图.
该算法可以称为加点法,每次迭代选择代价最小的边对应的点,将满足条件的加入到最小生成树中.算法从某个顶点 s出发,逐渐覆盖整个连通网的所有顶点.
- 图的所有顶点集合为 V,初始令顶点的集合
u={s}
,剩余的顶点集合 v=V−u; - 在两个集合 u,v能够组成的边中,选择一条代价最小的边 e=(u0,v0),并把这条边加入到最小生成树中,把 v0加入到集合 u中;
- 重复上述步骤,直到最小生成树有 n−1条边或者有 n个顶点为止.
Kruskal算法
该算法的时间复杂度为 O(|E|log2|E|),适用于边稀疏而顶点较多的图.
该算法可以称为 加边法 ,初始化最小生成树边数为 0,每迭代一次选择一条满足条件的最小代价边,加入到最小生成树的边集合中.
- 把图中的所有边按代价从小到大排序;
- 把图中的 n个顶点看成独立的 n棵树组成的森林;
- 按权值从小到大选择边,所选的边连接的两个顶点 ui,vi属于两棵不同的树,使之成为最小生成树的一条边,并将这两棵树合并作为一棵树(此处用到并查集);
- 重复 (3),直到所有顶点都在同一棵树上或者有 n−1条边为止.
最短路径
从图中某个顶点出发到达另外一个顶点的所经过的边的权重和最小的一条路径,称为最短路径.
单源最短路径问题(Single Source Shortest Path
, SSSP问题),给定一张有向图 G=(V,E), V是点集, E是边集, |V|=n, |E|=m,结点以 [1,n]之间的连续整数编号, (x,y,z)描述一条从 x出发,到达 y,长度为 z的有向边.设 1号点为起点,求长度为 n的数组 dist,其中 dist[i]表示从 1到结点 i的最短路径的长度.
Dijkstra算法
Dijkstra算法步骤如下:
- 初始化起点 start的 dist[start]=0,其余结点的 dist的值为正无穷大( +∞);
- 找出一个未被标记的且 dist[x]最小的结点 x,然后标记结点 x;
- 扫描结点 x的所有出边 (x,y,z),若 dist[y]>dist[x]+z,则使用 dist[x]+z更新 dist[y];
- 重复上述 2∼3两个步骤,直到所有结点都被标记.
Dijkstra算法基于贪心的思想,它只适用于所有边的长度都是非负数的图.当边长 z都是非负数时,全局最小值不可能再被其他结点更新,故在第一步选出的结点 x必然满足: dist[x]已经是起点到 x的最短路径.我们不断选择全局最小值进行标记和扩展,最终可得到起点 start到每一个结点的最短路径的长度.
举例:
- 用一个 dis数组保存源点到其余各个结点的距离, dis[i]表示源点到结点 i的距离,初始时 dis数组的各个元素为无穷大( +∞);用一个数组 flag标记是否找到了源点到该结点的最短距离,初始时 flag数组的各个元素清为 0;
![]()
- 假设源点是 1,将其 dis[1]设置为 0(源点到源点距离为 0);
![]()
- 遍历 dis数组,找到一个结点,这个结点是:没有确定最短路径的结点中距离源点最近的点.假设该结点编号为 i,此时就找到了源点到该结点的最短距离, flag[i]设置为 1;
- 遍历 i所有可以到达的结点 j,如果 dis[j]>dis[i]+w[i][j]( w[i][j]表示 i→j的距离),则更新 dis[j]为 dis[i]+w[i][j];
- 重复 3∼4步骤,直到所有结点的 flag都标记为 1.
![]()
![]()
![]()
- 此时 dis数组中,就保存了源点到其余各个结点的最短距离.
![]()
Floyd算法
为了求出图中任意两点间的最短路径,当然可以把每个点作为起点,求解 N次单源最短路径问题.不过,在任意两点间最短路问题中,图一般比较稠密.使用 Floyd算法可以在 O(n3)的时间内完成求解.
- 初始化邻接矩阵(二维数组) dist[][],其中 dist[i][j]表示顶点 i到顶点 j的权值,若顶点 i和顶点 j不相邻,则 dist[i][j]=+∞,若顶点 i等于顶点 j,则 dist[i][j]=0;
- 以第 1个顶点为中介点,若 dist[i][j]>dist[i][1]+dist[1][j],更新 dist[i][j];
- 依次以第 2,3,⋯,k,⋯,n个顶点为中介点,若 dist[i][j]>dist[i][k]+dist[k][j],更新 dist[i][j].
举例:
设 dist[k,i,j]表示经过若干个编号不超过k的结点
,从 i到 j的最短路长度.该问题可划分为两个子问题,经过编号不超过 k−1的结点从 i到 j,或者从 i到 k,再到 j,可得:
dist[k,i,j]=min(dist[k−1,i,j],dist[k−1,i,k]+dist[k−1,k,j])
初值为 dist[0,i,j]=A[i,j],其中 A为该图的邻接矩阵.
可以看到, Floyd算法的本质是动态规划, k是阶段,应置于最外层循环中, i和 j是附加状态,应置于内层循环.故不应该采用 i,j,k的顺序执行循环,会得到错误的答案. k这一维可省略(三维变成二维),最初,可直接用 dist保存邻接矩阵,然后执行动态规划的过程.当最外层循环到 k时,内层有转移方程: dist[i,j]=min(dist[i,j],dist[i,k]+dist[k,j]).
有向无环图描述表达式
若一个有向图中不存在环,则称为有向无环图,简称 DAG图.
将表达式转化为有向无环图步骤:
- 把各个操作数不重复地排成一排;
- 标出各个运算符的生效顺序(先后顺序有点出入无所谓);
- 按顺序加入运算符,注意分层;
- 从底向上逐层检查同层的运算符是否可以合体.
以
(a*b)*(a*b)*(a*b)*c
为例
- 第一步:把各个操作数不重复地排成一排
![]()
- 第二步:标出各个运算符的生效顺序(先后顺序有点出入无所谓)
![]()
- 第三步:按顺序加入运算符,注意分层
![]()
- 第四步:从底向上逐层检查同层的运算符是否可以合体
![]()
![]()
拓扑排序
在图论中,拓扑排序是一个有向无环图的所有顶点的线性序列,且该序列必须满足以下两个条件:
- 每个顶点出现且出现一次;
- 若存在一条从顶点 A到顶点 B的路径,那么在序列中顶点 A出现在顶点 B的前面.
有向无环图( DAG)才有拓扑排序,非 DAG无拓扑排序.例如下图中存在拓扑排序( A,B,C,D或 A,C,B,D可说明拓扑排序不唯一)
又如下图中不存在拓扑排序(存在 B,C,D构成的环,可说明拓扑排序可判断有向图是否有环)
拓扑排序的求法及步骤
- 该图中选择一个没有前驱(即入度为 0的顶点并记录该顶点;
- 从图中删除该顶点和所有以它为起点的有向边;
- 重复上述步骤,直到当前的图为空或者当前图中不存在没有前驱(入度为0)的顶点为止,如果满足后一种情况说明有向图必然存在环.
关键路径
在带权有向图,以顶点表示事件,以有向边表示活动,以边上的权值表示完成该活动的开销(如完成活动所需的时间),称之为用边表示活动的网络,简称 AOE网(Activity On Edge NetWork
).在 AOV网中的边无权值,仅表示顶点之间的前后关系.
在 AOE网具有以下两个性质:
- 只有在某顶点所代表的事件发生后,从该顶点出发的各有向边所代表的活动才能开始;
- 只有在进入某顶点的各有向边所代表的活动都已结束时,该顶点所发表的事件才能发生.
在 AOE网中仅有一个入度为 0的顶点,称为开始顶点(源点),它表示整个工程的开始;网中也仅存在一个出度为 0的顶点,称为结束顶点(汇点),它表示整个工程的结束.
从源点到汇点的有向路径可能有多条,所有路径中,具有最大路径长度的路径称为关键路径,而把关键路径上的活动称为关键活动.完成整个工程的最短时间就是关键路径的长度,若关键活动不能按时完成,则整个工程的完成时间就会延长.
- 事件 vk的最早发生时间 ve(k)
它是指从源点 v1到顶点 vk的最长路径长度.事件 vk的最早发生时间决定了所有从 vk开始的活动能够开工的最早时间.
ve(源点)=0;
ve(k)=max(ve(j)+weight(vj,vk)), vk为 vj的任意后继, weight(vj,vk)表示 ⟨vj,vk⟩上的权值.
计算 ve()值时,按从前往后的顺序进行,可以在拓扑排序的基础上计算:
- 初始时,令 ve[1,⋯,n]=0;
- 输入一个入度为 0的顶点 vj时,计算它所有直接后继顶点 vk的最早发生时间,若 ve[j]+weight(vj,vk)>ve[k],则 ve[k]=ve[k]+weight(vj,vk).以此类推,直至输出全部顶点.
- 事件 vk的最迟发生时间 vl(k)
它是指不推迟整个工程完成的前提下,即保证它的后继事件 vj在其最迟发生时间 vl(j)能够发生时,该事件最迟必须发生的时间.
- vl(汇点)=ve(汇点);
- vl(k)=min(vl(j)−weight(vk,vj)), vk为 vj的任意前驱.
计算 vl()值时,按从后往前的顺序进行,可以在逆拓扑排序(在上述拓扑排序中,增设一个栈以记录拓扑序列,拓扑排序结束后从栈顶至栈底为逆拓扑有序序列)的基础上计算.
- 初始时,令 vl(1,⋯,n)=ve[n];
- 栈顶顶点 vj出栈,计算其所有直接前驱顶点 vk的最迟发生时间,若 vl[j]−weight(vk,vj)<vl[k],则 vl[k]=vl[j]−weight(vk,vj).以此类推,直至输出全部栈中顶点.
- 活动 ai的最早开始时间 e(i)
它是指该活动弧的起点所表示的事件的最早发生时间. 若边 ⟨vk,vj⟩表示活动 ai,则有 e(i)=ve(k).
- 活动 ai的最迟开始时间 l(i)
它是指该活动弧的终点所表示的事件的最迟发生时间与该活动所需时间之差.若边 ⟨vk,vj⟩表示活动 ai,则有 l(i)=vl(j)−weight(vk,vj).
- 一个活动 ai的最迟开始时间 l(i)和其最早开始时间 e(i)的差额 d(i)=l(i)−e(i)
它是指活动完成的时间余量,即在不增加完成整个工程所需总时间的情况下,活动 ai可以拖延的时间.若一个活动的时间余量为零,则说明该活动必须要如期完成,否则就会拖延整个工程的进度,故称 l(i)−e(i)=0即 l(i)=e(i)的活动 ai是关键活动.
求解关键路径的步骤:
- 从源点出发,令 ve(源点)=0,按拓扑有序求其余顶点(事件)的最早发生时间 ve().
- 从汇点出发,令 vl(汇点)=ve(汇点),按逆拓扑有序求其余顶点(事件)的最迟发生时间 vl().
- 根据各顶点(事件)的 ve()值求所有弧(活动)的最早开始时间 e().
- 根据各顶点(事件)的 vl()值求所有弧(活动)的最迟开始时间 l().
- 求 AOE网中所有活动的差额 d(),找出所有 d()=0的活动构成关键路径.
举例:
![]()
拓扑序列为 V1,V2,V3,V4,V5,V6(其中 V2,V3可相互交换, V4,V5可相互交换)
首先计算事件最早发生时间 ve()(考虑到达 Vi的边)
对于事件 V1,由于 V1是源点,故 ve(1)=0;
对于事件 V2,仅存在从 V1到 V2的边 a1=3,故 ve(2)=ve(1)+a1=3;
对于事件 V3,仅存在从 V1到 V3的边 a2=2,故 ve(3)=ve(1)+a2=2;
对于事件 V4,存在从 V2到 V4的边 a3=2和从 V3到 V4的边 a5=4, 两者取最大值,故 ve(4)=max(ve(2)+a3,ve(3)+a5)=max(5,6)=6;
对于事件 V5,仅存在从 V2到 V5的边 a4=3,故 ve(5)=ve(2)+a4=6;
对于事件 V6,存在从 V3到 V6的边 a6=3、从 V4到 V6的边 a7=2和从 V5到 V6的边 a8=1,三者取最大值,故 ve(6)=max(ve(3)+a6,ve(4)+a7,ve(5)+a8)=max(5,8,7)=8.
然后计算事件最迟发生时间 vl()(考虑从 Vi出发的边)
- 对于事件 V6,由于 V6是汇点,故 vl(6)=ve(6)=8;
- 对于事件 V5,仅存在从 V5到 V6的边 a8=1,故 vl(5)=vl(6)−a8=7;
- 对于事件 V4,仅存在从 V4到 V6的边 a7=2,故 vl(4)=vl(6)−a7=6;
- 对于事件 V3,存在从 V3到 V6的边 a6=3和从 V3到 V4的边 a5=4,两者取最小值,故 vl(3)=min(vl(6)−a6,vl(4)−a5)=min(5,2)=2;
- 对于事件 V2,存在从 V2到 V4的边 a3=2和从 V2到 V5的边 a4=3,两者取最小值,故 vl(2)=min(vl(4)−a3,vl(5)−a4)=min(4,4)=4;
- 对于事件 V1,存在从 V1到 V2的边 a1=3和从 V1到 V3的边 a2=2,两者取最小值,故 vl(1)=min(vl(2)−a1,vl(3)−a2)=min(1,0)=0.
接着计算活动最早开始时间 e()和最迟开始时间 l()(最早看弧起点,最迟看弧终点)
对于活动 a1,弧起点为 V1,弧终点为 V2,故 e(1)=ve(1)=0, l(1)=vl(2)−a1=1;
对于活动 a2,弧起点为 V1,弧终点为 V3,故 e(2)=ve(1)=0, l(2)=vl(3)−a2=0;
对于活动 a3,弧起点为 V2,弧终点为 V4,故 e(3)=ve(2)=3, l(3)=vl(4)−a3=4;
对于活动 a4,弧起点为 V2,弧终点为 V5,故 e(4)=ve(2)=3, l(4)=vl(5)−a4=4;
对于活动 a5,弧起点为 V3,弧终点为 V4,故 e(5)=ve(3)=2, l(5)=vl(4)−a5=2;
对于活动 a6,弧起点为 V3,弧终点为 V6,故 e(6)=ve(3)=2, l(6)=vl(6)−a6=5;
对于活动 a7,弧起点为 V4,弧终点为 V6,故 e(7)=ve(4)=6, l(7)=vl(6)−a7=6;
对于活动 a8,弧起点为 V5,弧终点为 V6,故 e(8)=ve(5)=6,l(8)=vl(6)−a8=7.
最后计算 最迟开始时间 l()和其最早开始时间 e()的差额 d()=l()−e()(根据 l()是否与 e()相等)
- 对于活动 a1, e(1)≠l(1),故活动 a1不是关键活动;
- 对于活动 a2, e(2)=l(2),故活动 a2是关键活动;
- 对于活动 a3, e(3)≠l(3),故活动 a3不是关键活动;
- 对于活动 a4, e(4)≠l(4),故活动 a4不是关键活动;
- 对于活动 a5, e(5)=l(5),故活动 a5是关键活动;
- 对于活动 a6, e(6)≠l(6),故活动 a6不是关键活动;
- 对于活动 a7, e(7)≠l(7),故活动 a7是关键活动;
- 对于活动 a8, e(8)≠l(8),故活动 a8不是关键活动.
综上,故 a2,a5,a7为关键活动, (V1,V3,V4,V6)为关键路径.
![]()
关键路径上的所有活动都是关键活动,它是决定整个工程的关键因素,因此可以通过加快关键活动来缩短整个工程的工期.但也不能任意缩短关键活动,因为一旦缩短到一定的程度,该关键活动就能变成非关键活动.
网中关键路径并不唯一,且对于有几条关键网络的网,只提高一条关键路径上的关键活动速度并不能缩短整个工程的工期,只有加快那些包括在所有关键路径上的关键活动才能达到缩短工期的目的.
已知带权连通无向图 G=(V,E),其中
V={
v1,v2,v3,v4,v5,v6,v7}
,E={
(v1,v2)10,(v1,v3)2,(v3,v4)2,(v3,v6)11, (v2,v5)1,(v4,v5)4,(v4,v6)6,(v5,v7)7,(v6,v7)3}
(注:顶点偶对括号外的数据表示边上的权值),从源点 v1到顶点 v7的最短路径上经过的顶点序列是().A. v1,v2,v5,v7
B. v1,v3,v4,v6,v7
C. v1,v3,v4,v5,v7
D. v1,v2,v5,v4,v6,v7
- 选项 A,对于顶点序列 v1,v2,v5,v7,路径和为 10+1+7=18;
- 选项 B,对于顶点序列 v1,v3,v4,v6,v7,路径和为 2+2+6+3=13;
- 选项 C,对于顶点序列 v1,v3,v4,v5,v7,路径和为 2+2+4+7=15;
- 选项 D,对于顶点序列 v1,v2,v5,v4,v6,v7,路径和为 10+1+4+6+3=24;
故最短路径为 v1→v3→v4→v6→v7.
下图所示有向图的所有拓扑序列共有()个.
![]()
A. 4
B. 6
C. 5
D. 7
拓扑排序序列可能如下(共 5种):
- ABCDEFG
- ABCDFEG
- ABCFDEG
- ABDCEFG
- ABDCFEG
下列关于图的说法中,正确的是( C).
Ⅰ. 有向图中顶点 V的度等于其邻接矩阵中第 V行中 1的个数
Ⅱ. 无向图的邻接矩阵一定是对称矩阵,有向图的邻接矩阵一定是非对称矩阵
Ⅲ. 在带权图 G的最小生成树 G1中,某条边的权值可能会超过未选边的权值
Ⅳ. 若有向无环图的拓扑序列唯一,则可以唯一确定该图
A. Ⅰ、Ⅱ和Ⅲ
B. Ⅲ和Ⅳ
C. Ⅲ
D. Ⅳ
- 对于Ⅰ,邻接矩阵中第 V行中 1的个数表示有向图中顶点 V的出度,邻接矩阵中第 V列中 1的个数表示有向图中顶点 V的入度,而有向图中顶点的度为入度和出度的和,故Ⅰ错误;
- 对于Ⅱ,无向图可看作特殊的有向图,故有向图不一定是非对称矩阵,故Ⅱ错误;
- 对于Ⅲ,最小生成树中的 n−1条边并不能保证是图中权值最小的 n−1条边,因为权值最小的 n−1条边并不一定使得图连通,故Ⅲ正确;
- 对于Ⅳ,有向无环图的拓扑序列唯一并不能唯一确定该图,故Ⅳ错误.
若某带权图为 G=(V,E),其中
V={
v1,v2,v3,v4,v5,v6,v7,v8,v9,v10}
,E={
⟨v1,v2⟩5,⟨v1,v3⟩6,⟨v2,v5⟩3,⟨v3,v5⟩6,⟨v3,v4⟩3, ⟨v4,v5⟩3,⟨v4,v7⟩1,⟨v4,v8⟩4,⟨v5,v6⟩4,⟨v5,v7⟩2,⟨v6,v10⟩4, ⟨v7,v9⟩5,⟨v8,v9⟩2,⟨v9,v10⟩2}
(注:边括号外的数据表示边上的权值),则 G的关键路径的长度为().A. 19
B. 20
C. 21
D. 22
关键路径为 (v1,v3,v4(可省略),v5,v7,v9,v10),关键路径长度为 6+6(或3+3)+2+5+2=21.
下面关于求关键路径的说法中,不正确的是( C)
A. 求关键路径是以拓扑排序为基础的
B. 一个事件的最早发生时间与该事件为始的弧的活动的最早开始时间相同
C. 一个事件的最迟发生时间是以该事件尾的弧的活动的最迟开始时间与该活动的持续时间的差
D. 关键活动一定位于关键路径上
一个事件的最迟发生时间 =min {
以该事件为尾(出发)的弧的活动的最迟开始时间 }
或 min {
以该事件为尾(出发)的弧所指的事件的最迟发生时间与该弧的活动的持续时间之差 }
.
2010统考真题:对下图进行拓扑排序,可得不同拓扑序列的个数是().
![]()
A. 4
B. 3
C. 2
D. 1
拓扑排序序列可能如下(共 3种):
- aebcd
- abced
- abecd
2012统考真题:对下图所示的有向带权图,若采用 Dijkstra算法从源点 a到其他各顶点的最短路径,则得到的第一条最短路径的目标顶点是 b,第二条最短路径的目标顶点是 c,后续得到的其余各最短路径的目标顶点依次是().
![]()
A. d,e,f
B. e,d,f
C. f,d,e
D. f,e,d
可通过下表可看出最后选择的目标顶点依次是 f,d,e.
2013统考真题:下列 AOE网表示一项包含 8个活动的工程,通过同时加快若干活动的进度可以缩短整个工程的工期.下列选项中,可以加快其进度就可以缩短工程工期的是().
![]()
A. c和e
B. d和c
C. f和d
D. f和h
拓扑序列为 1,3,2,4,5,6(其中 4,5可交换)
首先计算事件最早发生时间 ve()(考虑到达 Vi的边)
- 对于事件 1,由于 1是源点,故 ve(1)=0;
- 对于事件 3,仅存在从 1到 3的边 b=8,故 ve(3)=ve(0)+b=8;
- 对于事件 2,存在从 1到 2的边 a=3和从 3到 2的边 d=4, 两者取最大值,故 ve(2)=max(ve(1)+a,ve(3)+d)=max(3,12)=12;
- 对于事件 4,仅存在从 2到 4的边 c=9,故 ve(4)=ve(2)+c=21;
- 对于事件 5,存在从 2到 5的边 e=6和从 3到 5的边 f=10, 两者取最大值,故 ve(5)=max(ve(2)+e,ve(3)+f)=max(18,18)=18;
- 对于事件 6,存在从 4到 6的边 g=6和从 5到 6的边 h=9,两者取最大值,故 ve(6)=max(ve(4)+g,ve(5)+h)=max(27,27)=27.
然后计算事件最迟发生时间 vl()(考虑从 Vi出发的边)
- 对于事件 6,由于 6是汇点,故 vl(6)=ve(6)=27;
- 对于事件 5,仅存在从 5到 6的边 h=9,故 vl(5)=vl(6)−h=18;
- 对于事件 4,仅存在从 4到 6的边 g=6,故 vl(4)=vl(6)−g=21;
- 对于事件 2,存在从 2到 4的边 c=9和从 2到 5的边 e=6,两者取最小值,故 vl(2)=min(vl(4)−c,vl(5)−e)=min(12,12)=12;
- 对于事件 3,存在从 3到 5的边 f=10和从 3到 2的边 d=4,两者取最小值,故 vl(3)=min(vl(5)−f,vl(2)−d)=min(8,8)=8;
- 对于事件 1,存在从 1到 2的边 a=3和从 1到 3的边 b=8,两者取最小值,故 vl(1)=min(vl(2)−a,vl(3)−b)=min(9,0)=0.
计算事件的最早发生时间和最迟发生时间如下表:
接着计算活动最早开始时间 e()和最迟开始时间 l()(最早看弧起点,最迟看弧终点)
- 对于活动 a,弧起点为 1,弧终点为 2,故 e(a)=ve(1)=0, l(a)=vl(2)−a=9;
- 对于活动 b,弧起点为 1,弧终点为 3,故 e(b)=ve(1)=0, l(b)=vl(3)−b=0;
- 对于活动 c,弧起点为 2,弧终点为 4,故 e(c)=ve(2)=12, l(c)=vl(4)−c=12;
- 对于活动 d,弧起点为 3,弧终点为 2,故 e(d)=ve(3)=8, l(d)=vl(2)−d=8;
- 对于活动 e,弧起点为 2,弧终点为 5,故 e(e)=ve(2)=12, l(e)=vl(5)−e=12;
- 对于活动 f,弧起点为 3,弧终点为 5,故 e(f)=ve(3)=8, l(f)=vl(5)−f=8;
- 对于活动 g,弧起点为 4,弧终点为 6,故 e(g)=ve(4)=21, l(g)=vl(6)−g=21;
- 对于活动 h,弧起点为 5,弧终点为 6,故 e(h)=ve(5)=18, l(h)=vl(6)−h=18.
计算活动的最早开始时间和最迟开始时间如下表:
故可得出全部关键活动为 b,d,c,g和 b,d,e,h和 b,f,h.只有关键路径上的活动时间同时减少时,才能缩短工期.而当 f和 d同时减少会缩短工期.
2012统考真题:若用邻接矩阵存储有向图,矩阵中主对角线以下的元素均为零,则关于该图拓扑序列的结论是( C).
A. 存在,且唯一
B. 存在,且不唯一
C. 存在,可能不唯一
D. 无法确定是否存在
对角线以下的元素均为零,表明只有从顶点 i到顶点 j( i<j)可能有边,而从顶点 j到顶点 i一定无边,即有向图是一个无环图,故一定存在拓扑序列.
对于下图对应的拓扑序列为 1,2,3或 1,3,2,故此时存在不唯一的拓扑序列;若对角线以上的元素均为 1,以下元素全为 0,则拓扑序列唯一.
2014统考真题:对下图所示的有向图进行拓扑排序,得到的拓扑序列可能是().
![]()
A. 3,1,2,4,5,6
B. 3,1,2,4,6,5
C. 3,1,4,2,5,6
D. 3,1,4,2,6,5
该图的拓扑序列为 3,1,4,2,6,5( 2,6可交换顺序)
2015统考真题:求下面的带权图的最小(代价)生成树时,可能是 Kruskal算法第 2次选中但不是 Prim算法(从 V4开始)第 2次选中的边是().
![]()
A. (V1,V3)
B. (V1,V4)
C. (V2,V3)
D. (V3,V4)
对于 Kruskal算法第一步会选择 (V1,V4),第二歩会选择 (V1,V3)或 (V2,V3)或者 (V3,V4);而对于 Prim算法第一步会选择 (V1,V4),第二歩会选择 (V1,V3)或者(V3,V4).
2016统考真题:使用 Dijkstra算法求下图中从顶点 1到其他各顶点的最短路径,依次得到的各最短路径的目标顶点是().
![]()
A. 5,2,3,4,6
B. 5,2,3,6,4
C. 5,2,4,3,6
D. 5,2,6,3,4
可通过下表可看出目标顶点(除去起点)依次是 5,2,3,6,4
2018统考真题:下列选项中,不是如下有向图的拓扑序列的是().
![]()
A. 1,5,2,3,6,4
B. 5,1,2,6,3,4
C. 5,1,2,3,6,4
D. 5,2,1,6,3,4
存在的拓扑序列如下(共 4种)
- 1,5,2,3,6,4
- 1,5,2,6,3,4
- 5,1,2,3,6,4
- 5,1,2,6,3,4
拓扑排序每次选取入度为 0的结点输出,不难发现拓扑序列前两位一定是 1,5或 5,1(因为只有 1和 5的入度均为 0,且其他结点都不满足仅有 1或仅有 5作为前驱),故 D错误.
2019统考真题:下图所示的 AOE网表示一项包含 8个活动的工程,活动 d的最早开始时间和最迟开始时间分别是()
![]()
A. 3和7
B. 12和12
C. 12和14
D. 15和15
拓扑序列为 1,3,2,4,5,6(其中 4,5可交换)——该图和前面2013统考真题差别在于 2到 4的边权有变化,且 b,c,d编号有出入
首先计算事件最早发生时间 ve()(考虑到达 Vi的边)
- 对于事件 1,由于 1是源点,故 ve(1)=0;
- 对于事件 3,仅存在从 1到 3的边 c=8,故 ve(3)=ve(0)+c=8;
- 对于事件 2,存在从 1到 2的边 a=3和从 3到 2的边 b=4, 两者取最大值,故 ve(2)=max(ve(1)+a,ve(3)+b)=max(3,12)=12;
- 对于事件 4,仅存在从 2到 4的边 d=7,故 ve(4)=ve(2)+d=19;
- 对于事件 5,存在从 2到 5的边 e=6和从 3到 5的边 f=10, 两者取最大值,故 ve(5)=max(ve(2)+e,ve(3)+f)=max(18,18)=18;
- 对于事件 6,存在从 4到 6的边 g=6和从 5到 6的边 h=9,两者取最大值,故 ve(6)=max(ve(4)+g,ve(5)+h)=max(27,27)=27.
然后计算事件最迟发生时间 vl()(考虑从 Vi出发的边)
- 对于事件 6,由于 6是汇点,故 vl(6)=ve(6)=27;
- 对于事件 5,仅存在从 5到 6的边 h=9,故 vl(5)=vl(6)−h=18;
- 对于事件 4,仅存在从 4到 6的边 g=6,故 vl(4)=vl(6)−g=21;
- 对于事件 2,存在从 2到 4的边 d=7和从 2到 5的边 e=6,两者取最小值,故 vl(2)=min(vl(4)−d,vl(5)−e)=min(14,12)=12;
- 对于事件 3,存在从 3到 5的边 f=10和从 3到 2的边 b=4,两者取最小值,故 vl(3)=min(vl(5)−f,vl(2)−b)=min(8,8)=8;
- 对于事件 1,存在从 1到 2的边 a=3和从 1到 3的边 c=8,两者取最小值,故 vl(1)=min(vl(2)−a,vl(3)−c)=min(9,0)=0.
计算事件的最早发生时间和最迟发生时间如下表:
接着计算活动最早开始时间 e()和最迟开始时间 l()(最早看弧起点,最迟看弧终点)
- 对于活动 a,弧起点为 1,弧终点为 2,故 e(a)=ve(1)=0, l(a)=vl(2)−a=9;
- 对于活动 b,弧起点为 3,弧终点为 2,故 e(b)=ve(3)=8, l(b)=vl(2)−b=8;
- 对于活动 c,弧起点为 1,弧终点为 3,故 e(c)=ve(1)=0, l(c)=vl(3)−c=0;
- 对于活动 d,弧起点为 2,弧终点为 4,故 e(d)=ve(2)=12, l(d)=vl(4)−d=14;
- 对于活动 e,弧起点为 2,弧终点为 5,故 e(e)=ve(2)=12, l(e)=vl(5)−e=12;
- 对于活动 f,弧起点为 3,弧终点为 5,故 e(f)=ve(3)=8, l(f)=vl(5)−f=8;
- 对于活动 g,弧起点为 4,弧终点为 6,故 e(g)=ve(4)=19, l(g)=vl(6)−g=21;
- 对于活动 h,弧起点为 5,弧终点为 6,故 e(h)=ve(5)=18, l(h)=vl(6)−h=18.
计算活动的最早开始时间和最迟开始时间如下表:
故由表知,活动 d的最早开始时间和最迟开始时间分别为 12,14.
2019统考真题:用有向无环图描述表达式 (x+y)((x+y)/x),需要的顶点个数至少是().
A. 5
B. 6
C. 8
D. 9
首先将该表达式转换成有向二叉树,然后将有向二叉树去重转换成有向无环图,故化简后至少需要 5个顶点.
2020统考真题:已知无向图 G如下所示,使用克鲁斯卡尔( Kruskal)算法求图 G的最小生成树,加到最小生成树的边依次是().
![]()
A. (b,f),(b,d),(a,e),(c,e),(b,e)
B. (b,f),(b,d),(b,e),(a,e),(c,e)
C. (a,e),(b,e),(c,e),(b,d),(b,f)
D. (a,e),(c,e),(b,e),(b,f),(b,d)
由该图知,加到最小生成树的边依次 (b,f),(b,d),(a,e),(c,e),(b,e).
2021统考真题:给定如下有向图,该图的拓扑有序序列的个数是().
![]()
A. 1
B. 2
C. 3
D. 4
该图的拓扑有序序列仅为 A,B,C,D,E,F,故只有 1个.
2021统考真题:使用 Dijkstra算法求下图中从顶点 1到其余各顶点的最短路径,将当前找到的从顶点 1到顶点 2,3,4,5的最短路径长度保存在数组 dist中,求出第二条最短路径后, dist中的内容更新为().
![]()
A. 26,3,14,6
B. 25,3,14,6
C. 21,3,14,6
D. 15,3,14,6
由表知求出第二条最短路径后, dist中的内容更新为 21,3,14,6
2022统考真题:下图是一个有 10个活动的 AOE网,时间余量最大的活动是().
![]()
A. c
B. g
C. h
D. j
拓扑序列为 1,2,3,4,5,6
首先计算事件最早发生时间 ve()(考虑到达 Vi的边)
- 对于事件 1,由于 1是源点,故 ve(1)=0;
- 对于事件 2,仅存在从 1到 2的边 a=2,故 ve(2)=ve(1)+a=2;
- 对于事件 3,存在从 1到 3的边 b=5和从 2到 3的边 c=1, 两者取最大值,故 ve(3)=max(ve(1)+b,ve(2)+c)=max(5,3)=5;
- 对于事件 4,存在从 2到 4的边 d=3和从 3到 4的边 e=3, 两者取最大值,故 ve(4)=max(ve(2)+d,ve(3)+e)=max(5,8)=8;
- 对于事件 5,存在从 3到 5的边 f=4和从 4到 5的边 h=1, 两者取最大值,故 ve(5)=max(ve(3)+f,ve(4)+h)=max(9,9)=9;
- 对于事件 6,存在从 3到 6的边 g=1、从 4到 6的边 i=4和从 5到 6的边 j=1, 三者取最大值,故 ve(3)=max(ve(3)+g,ve(4)+i,ve(5)+j)=max(6,12,10)=12;
然后计算事件最迟发生时间 vl()(考虑从 Vi出发的边)
- 对于事件 6,由于 6是汇点,故 vl(6)=ve(6)=12;
- 对于事件 5,仅存在从 5到 6的边 j=1,故 vl(5)=vl(6)−j=11;
- 对于事件 4,存在从 4到 5的边 h=1和从 4到 6的边 i=4,两者取最小值,故 vl(4)=min(vl(5)−h,vl(6)−i)=min(10,8)=8;
- 对于事件 3,存在从 3到 4的边 e=3、从 3到 5的边 f=4和从 3到 6的边 g=1,三者取最小值,故 vl(3)=min(vl(4)−e,vl(5)−f,vl(6)−g)=min(5,8,11)=5;
- 对于事件 2,存在从 2到 4的边 d=3和从 2到 3的边 c=1,两者取最小值,故 vl(2)=min(vl(4)−d,vl(3)−c)=min(5,4)=4;
- 对于事件 1,存在从 1到 2的边 a=2和从 1到 3的边 b=5,两者取最小值,故 vl(1)=min(vl(2)−a,vl(3)−b)=min(2,0)=0;
计算事件的最早发生时间和最迟发生时间如下表:
接着计算活动最早开始时间 e()和最迟开始时间 l()(最早看弧起点,最迟看弧终点)
- 对于活动 a,弧起点为 1,弧终点为 2,故 e(a)=ve(1)=0, l(a)=vl(2)−a=2;
- 对于活动 b,弧起点为 1,弧终点为 3,故 e(b)=ve(1)=0, l(b)=vl(3)−b=0;
- 对于活动 c,弧起点为 2,弧终点为 3,故 e(c)=ve(2)=2, l(c)=vl(3)−c=4;
- 对于活动 d,弧起点为 2,弧终点为 4,故 e(d)=ve(2)=2, l(d)=vl(4)−d=5;
- 对于活动 e,弧起点为 3,弧终点为 4,故 e(e)=ve(3)=5, l(e)=vl(4)−e=5;
- 对于活动 f,弧起点为 3,弧终点为 5,故 e(f)=ve(3)=5, l(f)=vl(5)−f=7;
- 对于活动 g,弧起点为 3,弧终点为 6,故 e(g)=ve(3)=5, l(g)=vl(6)−g=11;
- 对于活动 h,弧起点为 4,弧终点为 5,故 e(h)=ve(4)=8, l(h)=vl(5)−h=10;
- 对于活动 i,弧起点为 4,弧终点为 6,故 e(i)=ve(4)=8, l(i)=vl(6)−i=8;
- 对于活动 j,弧起点为 5,弧终点为 6,故 e(j)=ve(5)=9, l(j)=vl(6)−j=11.
计算活动的最早开始时间、最迟开始时间和时间余量(两者作差)如下表(关键路径为 1,3,4,6):
6.4.1
下面是一种称为
破圈法
的求解最小生成树的方法:所谓
破圈法
,是指任取一圈,去掉圈上权最大的边
,反复执行这一步骤,直到没有圈为止.试判断这种方法是否正确,若正确,说明理由;若不正确,举出反例(注:圈就是回路).
正确.
证明如下:
设 T为图 G的最小生成树, T′为破圈法得到的生成树
假设 T′不是最小生成树,则 ∃e=uv∈T且 e∉T′
则 T′+e,中含有一个圈 C,由破圈法得到 T′的过程易知 e∈C为圈 C中权值最大的边
此外,图 T−e包含两个连通片,设为 T1和 T2,且 u∈T1, v∈T2,则 C中必定存在一条边 e′=u′v′,其中 u′∈T1, v′∈T2,且 e′的权值小于等于 e的权值
若 e′的权值小于 e的权值,则 T−e+e′是图 G的一个权值比 T小的生成树,与 T是最小生成树矛盾
若 e′与 e的权值相等,则 T−e+e′也是图 G的一棵最小生成树,且相比于 T,T−e+e′与 T′的公共边数多一
a. 若 T−e+e′与 T′相同(公共边一样),则 T′与 T−e+e′权值相同,因此是最小生成树,与假设矛盾
b. 若 T−e+e′与 T′不同,则 \exists e’‘=u’‘v’‘ \in T-e+e’且 e \in T’,不断重复上述整个过程,每次可得到另一棵相比于 T-e+e’,与 T’的公共边数多一的最小生成树,直到得到一棵与 T’公共边完全相同的最小生成树,即说明 T’是最小生成树,与题设矛盾
书上的证明:
![]()
![]()
扩展:
破圈法的时间复杂度:需要删除 E-(V-1)条边,即重复寻找 E-(V-1)个圈,如果利用 DFS寻找圈,时间复杂度为 O(E+V),可以在 DFS过程中记录当前路径上权值最大的边,一般连通图 E > V,故时间复杂度为 O(E^2).
6.4.2
已知有向图如下图所示.
![]()
- 写出该图的邻接矩阵表示并据此给出从顶点 1出发的深度优先遍历序列.
- 写该有向图的强连通分量的数目.
- 给出该图的任意两个拓扑序列.
- 若将该图视为无向图,分别用 Prim算法和 Kruskal算法求最小生成树.
(1) 邻接矩阵如下:
深度优先遍历序列为 1,2,3,5,7,4,6
有向图强连通分量:在有向图 G中,如果两个顶点 v_i,v_j间( v_i > v_j)有一条从 v_i到 v_j的有向路径,同时还有一条从 v_j到 v_i的有向路径,则称两个顶点强连通.
(2) 当某个顶点只有出弧而没有入弧时,其他顶点无法到达这个顶点,不可能与其他顶点和边构成强连通分量(这个单独的顶点构成一个强连通分量).
- 顶点 1无入弧构成第一个强连通分量,删除顶点 1及所有以之为尾(出边)的弧;
- 顶点 2无入弧构成一个强连通分量,删除顶点 2及所有以之为尾的弧(出边)的弧;
-
\cdots
-
顶点 7无入弧构成一个强连通分量,删除顶点 7及所有以之为尾的弧(出边)的弧.
综上,每个顶点都是一个强连通分量.故强连通分量数目为 7.
(3) 1,2,4,6,3,5,7或 1,4,6,2,3,5,7或 1,4,2,6,3,5,7
(4) 假设是 Prim算法(以 3号结点开始),第一步选择边 (1,3),第二歩选择边 (1,2),第三步选择边 (3,6),第四步选择边 (3,5)(此时不会选择 (2,3),会构成环),第五步选择边 (5,7),最后选择 (4,6)(此时不会选择 (2,5),会构成环).
若是 Kruskal算法,此处选择比较随意,假设最先选择权值最小的边 (5,7),然后选择权值最小的边 (3,6),接着选择权值最小的边 (1,2),接着选择边 (1,3),接着选择边 (3,5)(此时不会选择 (2,3),会构成环),最后选择边 (4,6)(此时不会选择 (2,5),会构成环).
6.4.3
对下图所示的无向图,按照 Dijkstra算法,写出从顶点 1到其他各个顶点的最短路径和最短路径长度(顺序不能颠倒).
![]()
其中上方表示从 1出发的路径,下方表示对应的路径和
6.4.4
下图所示为一个用 AOE网表示的工程.
画出此图的邻接表表示.
完成次工程至少需要多少时间?
指出关键路径.
哪些活动加速可以缩短完成工程所需的时间?
![]()
(1) 邻接表如下
计算关键路径(为 2,3,4问作准备)
拓扑序列为 1,2,3,4,5,6,7,8,9( 4,5可交换)
首先计算事件最早发生时间 ve()(考虑到达 V_i的边)
- 对于事件 V_1,由于 V_1是源点,故 ve(1)=0;
- 对于事件 V_2,仅存在从 V_1到 \color{Red}{V_2}的边 a_1=2,故 ve(2)=ve(1)+a_1=2;
- 对于事件 V_3,存在从 V_1到 \color{Red}{V_3}的边 a_2=5和从 V_2到 \color{Red}{V_3}的边 a_5=2, 两者取最大值,故 ve(3)=max(ve(1)+a_2,ve(2)+a_5)=max(5,4)=5;
- 对于事件 V_4,仅存在从 V_2到 \color{Red}{V_4}的边 a_4=3,故 ve(4)=ve(2)+a_4=5;
- 对于事件 V_5,存在从 V_1到 \color{Red}{V_5}的边 a_3=5和从 V_3到 \color{Red}{V_5}的边 a_6=1, 两者取最大值,故 ve(5)=max(ve(1)+a_3,ve(3)+a_6)=max(5,6)=6;
- 对于事件 V_6,存在从 V_4到 \color{Red}{V_6}的边 a_8=2和从 V_3到 \color{Red}{V_6}的边 a_7=3, 两者取最大值,故 ve(6)=max(ve(4)+a_8,ve(3)+a_7)=max(7,8)=8;
- 对于事件 V_7,存在从 V_5到 \color{Red}{V_7}的边 a_9=6和从 V_6到 \color{Red}{V_7}的边 a_{11}=3, 两者取最大值,故 ve(7)=max(ve(5)+a_9,ve(6)+a_{11})=max(12,11)=12;
- 对于事件 V_8,仅存在从 V_6到 \color{Red}{V_8}的边 a_{10}=4,故 ve(8)=ve(6)+a_{10}=12;
- 对于事件 V_9,存在从 V_7到 \color{Red}{V_9}的边 a_{12}=4和从 V_8到 \color{Red}{V_9}的边 a_{13}=2, 两者取最大值,故 ve(9)=max(ve(7)+a_{12},ve(8)+a_{13})=max(16,14)=16.
然后计算事件最迟发生时间 vl()(考虑从 V_i出发的边)
- 对于事件 V_9,由于 V_9是汇点,故 vl(9)=ve(9)=16;
- 对于事件 V_8,仅存在从 \color{Green}{V_8}到 V_9的边 a_{13}=2,故 vl(8)=vl(9)-a_{13}=14;
- 对于事件 V_7,仅存在从 \color{Green}{V_7}到 V_9的边 a_{12}=4,故 vl(7)=vl(9)-a_{12}=12;
- 对于事件 V_6,存在从 \color{Green}{V_6}到 V_8的边 a_{10}=4和从 \color{Green}{V_6}到 V_7的边 a_{11}=3,两者取最小值,故 vl(6)=min(vl(8)-a_{10},vl(7)-a_{11})=min(10,9)=9;
- 对于事件 V_5,仅存在从 \color{Green}{V_5}到 V_7的边 a_9=6,故 vl(5)=vl(7)-a_9=6;
- 对于事件 V_4,仅存在从 \color{Green}{V_4}到 V_6的边 a_8=2,故 vl(4)=vl(6)-a_8=7;
- 对于事件 V_3,存在从 \color{Green}{V_3}到 V_5的边 a_6=1和从 \color{Green}{V_3}到 V_6的边 a_7=3,两者取最小值,故 vl(3)=min(vl(5)-a_6,vl(6)-a_7)=min(5,6)=5;
- 对于事件 V_2,存在从 \color{Green}{V_2}到 V_3的边 a_5=2和从 \color{Green}{V_2}到 V_4的边 a_4=3,两者取最小值,故 vl(2)=min(vl(3)-a_5,vl(4)-a_4)=min(3,4)=3;
- 对于事件 V_1,存在从 \color{Green}{V_1}到 V_2的边 a_1=2、从 \color{Green}{V_1}到 V_3的边 a_2=5和从 \color{Green}{V_1}到 V_5的边 a_3=5,三者取最小值,故 vl(1)=min(vl(2)-a_1,vl(3)-a_2,vl(5)-a_3)=min(1,0,1)=0.
计算事件的最早发生时间和最迟发生时间如下表:
接着计算活动最早开始时间 e()和最迟开始时间 l()(最早看弧起点,最迟看弧终点)
- 对于活动 a_1,弧起点为 1,弧终点为 2,故 e(a_1)=ve(1)=0, l(a_1)=vl(2)-a_1=1;
- 对于活动 a_2,弧起点为 1,弧终点为 3,故 e(a_2)=ve(1)=0, l(a_2)=vl(3)-a_2=0;
- 对于活动 a_3,弧起点为 1,弧终点为 5,故 e(a_3)=ve(1)=0, l(a_3)=vl(5)-a_3=1;
- 对于活动 a_4,弧起点为 2,弧终点为 4,故 e(a_4)=ve(2)=2, l(a_4)=vl(4)-a_4=4;
- 对于活动 a_5,弧起点为 2,弧终点为 3,故 e(a_5)=ve(2)=2, l(a_5)=vl(3)-a_5=3;
- 对于活动 a_6,弧起点为 3,弧终点为 5,故 e(a_6)=ve(3)=5, l(a_6)=vl(5)-a_6=5;
- 对于活动 a_7,弧起点为 3,弧终点为 6,故 e(a_7)=ve(3)=5, l(a_7)=vl(6)-a_7=6;
- 对于活动 a_8,弧起点为 4,弧终点为 6,故 e(a_8)=ve(4)=5, l(a_8)=vl(6)-a_8=7;
- 对于活动 a_9,弧起点为 5,弧终点为 7,故 e(a_9)=ve(5)=6, l(a_9)=vl(7)-a_9=6;
- 对于活动 a_{10},弧起点为 6,弧终点为 8,故 e(a_{10})=ve(6)=8, l(a_{10})=vl(8)-a_{10}=10;
- 对于活动 a_{11},弧起点为 6,弧终点为 7,故 e(a_{11})=ve(6)=8, l(a_{11})=vl(7)-a_{11}=9;
- 对于活动 a_{12},弧起点为 7,弧终点为 9,故 e(a_{12})=ve(7)=12, l(a_{12})=vl(9)-a_{12}=12;
- 对于活动 a_{13},弧起点为 8,弧终点为 9,故 e(a_{13})=ve(8)=12, l(a_{13})=vl(9)-a_{13}=14.
计算活动的最早开始时间和最迟开始时间如下表:
(2) 完成此工程至少需要时间的为 \color{Red}{16}(即事件 V_9的最早发生时间或最迟发生时间)
(3) 关键路径为 \color{Red}{(V_1,V_3,V_5,V_7,V_9)}
(4) 活动 \color{Red}{a_2,a_6,a_9,a_{12}}加速可以缩短完成工程所需的时间.
6.4.5
下表给出了某工程各工序之间的优先关系和各工序所需的时间(其中
-
表示无先驱工序),请完成以下各题:
- 画出相应的 AOE网.
- 列出各事件的最早发生时间和最迟发生时间.
- 求出关键路径并指明完成该工程所需的最短时间.
![]()
(1) AOE图如下:
计算关键路径
拓扑序列为 1,2,3,4,5,6( 2,3可交换, 4,5可交换)
首先计算事件最早发生时间 ve()(考虑到达 V_i的边)
- 对于事件 V_1,由于 V_1是源点,故 ve(1)=0;
- 对于事件 V_2,仅存在从 V_1到 \color{Red}{V_2}的边 A=3,故 ve(2)=ve(1)+A=3;
- 对于事件 V_3,仅存在从 V_1到 \color{Red}{V_3}的边 B=2,故 ve(3)=ve(1)+B=2;
- 对于事件 V_4,存在从 V_2到 \color{Red}{V_4}的边 C=2和从 V_3到 \color{Red}{V_4}的边 E=4, 两者取最大值,故 ve(4)=max(ve(2)+C,ve(3)+E)=max(5,6)=6;
- 对于事件 V_5,仅存在从 V_2到 \color{Red}{V_5}的边 D=3,故 ve(5)=ve(2)+D=6;
- 对于事件 V_6,存在从 V_2到 \color{Red}{V_6}的边 F=3、从 V_4到 \color{Red}{V_6}的边 G=2和从 V_5到 \color{Red}{V_6}的边 H=1, 三者取最大值,故 ve(6)=max(ve(2)+F,ve(4)+G,ve(5)+H)=max(6,8,7)=8.
然后计算事件最迟发生时间 vl()(考虑从 V_i出发的边)
- 对于事件 V_6,由于 V_6是汇点,故 vl(6)=ve(6)=8;
- 对于事件 V_5,仅存在从 \color{Green}{V_5}到 V_6的边 H=1,故 vl(5)=vl(6)-H=7;
- 对于事件 V_4,仅存在从 \color{Green}{V_4}到 V_6的边 G=2,故 vl(4)=vl(6)-G=6;
- 对于事件 V_3,仅存在从 \color{Green}{V_3}到 V_4的边 E=4,故 vl(3)=vl(4)-E=2;
- 对于事件 V_2,存在从 \color{Green}{V_2}到 V_4的边 C=2、从 \color{Green}{V_2}到 V_5的边 D=3和从 \color{Green}{V_2}到 V_6的边 F=3,三者取最小值,故 vl(2)=min(vl(4)-C,vl(5)-D,vl(6)-F)=min(4,4,5)=4;
- 对于事件 V_1,存在从 \color{Green}{V_1}到 V_2的边 A=3和从 \color{Green}{V_1}到 V_3的边 B=2,两者取最小值,故 vl(1)=min(vl(2)-A,vl(3)-B)=min(1,0)=0.
(2) 计算事件的最早发生时间和最迟发生时间如下表:
接着计算活动最早开始时间 e()和最迟开始时间 l()(最早看弧起点,最迟看弧终点)
- 对于活动 A,弧起点为 1,弧终点为 2,故 e(A)=ve(1)=0, l(A)=vl(2)-A=1;
- 对于活动 B,弧起点为 1,弧终点为 3,故 e(B)=ve(1)=0, l(B)=vl(3)-B=0;
- 对于活动 C,弧起点为 2,弧终点为 4,故 e(C)=ve(2)=3, l(C)=vl(4)-C=4;
- 对于活动 D,弧起点为 2,弧终点为 5,故 e(D)=ve(2)=3, l(D)=vl(5)-D=4;
- 对于活动 E,弧起点为 3,弧终点为 4,故 e(E)=ve(3)=2, l(E)=vl(4)-E=2;
- 对于活动 F,弧起点为 2,弧终点为 6,故 e(F)=ve(2)=3, l(F)=vl(6)-F=5;
-
对于活动 G,弧起点为 4,弧终点为 6,故 e(G)=ve(4)=6, l(G)=vl(6)-G=6;
-
对于活动 H,弧起点为 5,弧终点为 6,故 e(H)=ve(5)=6, l(H)=vl(6)-H=7.
计算活动的最早开始时间和最迟开始时间如下表:
(3) 关键路径为 \color{Red}{(V_1,V_3,V_4,V_6)},完成该工程所需的最短时间为 \color{Red}{8}(即事件 V_6的最早发生时间或最迟发生时间)
6.4.6
试说明利用 DFS如何实现有向无环图拓扑排序.
DFS 总是沿着一条路搜索到底,然后逐层回退,因此 DFS也适合拓扑排序,并且可以在回退的过程中保存下拓扑排序的逆序 .
int vis[MaxVertexNum];//1代表正在访问,-1代表访问结束,0代表未访问
int ans[MaxVertexNum],tot;
bool dfs_topsort(ALGraph G,int root)
{
vis[root]=1;//正在访问
for(int j=FirstNeighbor(G, root);j!=-1;j=NextNeighbor(G, root, j))
{
if(vis[j]==1)//如果后继比前驱先访问,说明存在环
return false;
if(vis[j]==0&&dfs_topsort(G, j)==false)//如果后继未被访问,访问后继返回假,也是失败
return false;
}
vis[root]=-1;
ans[tot++]=root;//在递归结束才加入拓扑序列数组中,最深层次先返回
return true;
}
for(int i=1;i<=alg.vexnum;i++)
if(vis[i]==0)
dfs_topsort(alg,i);
for(int i=tot-1;i>=0;i--)
cout << alg.vertices[ans[i]].data << " ";
cout << endl;
书上做法:
6.4.7
一连通无向图,边非负权值,问用 Dijkstra最短路径算法能否给出一棵生成树,该树是否一定是最小生成树?说明理由.
Dijkstra最短路径算法能够给出一棵生成树,但该树不一定为最小生成树.虽然 Dijkstra算法和 Prim算法的思路与步骤较为相似,但两者的更新算法不一致,而其余部分完全一致.( Dijkstra算法是局部最优, Prim算法是全局最优)
- 在 Dijkstra算法中,对应的 min更新算法为:
if(dist[j]>dist[k]+g[k][j])
dist[j]=dist[k]+g[k][j];
- 在 Prim算法中,对应的 min更新算法为:
if(dist[j]>g[k][j])
dist[j]=g[k][j]
对于以下的带权连通无向图
用 Dijkstra算法构造的一棵生成树为:
用 Prim算法构造的一棵生成树为:
在 Dijkstra算法的执行过程中,从 1到 3的最短路径选择的是 1 \to 3,而不是 1 \to 4 \to 3(或 1 \to 2 \to 3),原因是 dist[3]=dist[4]+g[4][3]=dist[2]+g[2][3],即 1到 3的初始最短距离与 1到 4的最短路径加上 4到 3的距离(或1到 2的最短路径加上 2到 3的距离)相等,因为在更新过程中保留 1 \to 3的最短路径为 1 \to 3而非 1 \to 4 \to 3(或 1 \to 2 \to 3).构造的生成树的边权值之和为 1+4+6=11,远大于用 Prim算法构造的最小生成树边权值之和 1+2+4=7.
6.4.8
2009统考真题:带权图(权值非负,表示边连接的两顶点间的距离)的最短路径问题是找出从初始顶点到目标顶点之间的一条最短路径.假设从初始顶点到目标顶点之间存在路径,现有一种解决该问题的方法:
- 设最短路径初始时仅包含初始顶点,令当前顶点 u为初始顶点.
- 选择离 u最近且尚未在最短路径中的一个顶点 v,加入最短路径,修改当前顶点 u=v.
- 重复步骤 2,直到 u是目标顶点为止.
请问上述方法能否求得到最短路径?若该方法可行,请证明;否则,请举例说明.
该方法不一定能求得最短路径.
对于下图所示的带权图,若按照题中的原则,从 A到 C的最短路径是 A \to B \to C,实际上是 A \to D \to C.
6.4.9
2011统考真题:已知有 6个顶点(顶点编号为 0 \sim 5)的有向带权图 G,其邻接矩阵 A为上三角矩阵,按行为主序(行优先)保存在如下的一维数组中.
要求:
- 写出图 G的邻接矩阵 A.
- 画出有向带权图 G.
- 求图 G的关键路径,并计算该关键路径的长度.
(1) 图 G的邻接矩阵如下
(2) 有向带权图 G如下
(3)计算关键路径
拓扑序列为 0,1,2,3,4,5( 3,4可交换)
首先计算事件最早发生时间 ve()(考虑到达 V_i的边)
- 对于事件 0,由于 0是源点,故 ve(0)=0;
- 对于事件 1,仅存在从 0到 \color{Red}{1}的边 a_{01}=4,故 ve(1)=ve(0)+a_{01}=4;
- 对于事件 2,存在从 0到 \color{Red}{2}的边 a_{02}=6和从 1到 \color{Red}{2}的边 a_{12}=5, 两者取最大值,故 ve(2)=max(ve(0)+a_{02},ve(1)+a_{12})=max(6,9)=9;
- 对于事件 3,仅存在从 2到 \color{Red}{3}的边 a_{23}=4,故 ve(3)=ve(2)+a_{23}=13;
- 对于事件 4,仅存在从 2到 \color{Red}{4}的边 a_{24}=3,故 ve(4)=ve(2)+a_{24}=12;
- 对于事件 5,存在从 3到 \color{Red}{5}的边 a_{35}=3和从 4到 \color{Red}{5}的边 a_{45}=3, 两者取最大值,故 ve(5)=max(ve(3)+a_{35},ve(4)+a_{45})=max(16,15)=16.
然后计算事件最迟发生时间 vl()(考虑从 V_i出发的边)
- 对于事件 5,由于 5是汇点,故 vl(5)=ve(5)=16;
- 对于事件 4,仅存在从 \color{Green}{4}到 5的边 a_{45}=3,故 vl(4)=vl(5)-a_{45}=13;
- 对于事件 3,仅存在从 \color{Green}{3}到 5的边 a_{35}=3,故 vl(3)=vl(5)-a_{35}=13;
- 对于事件 2,存在从 \color{Green}{2}到 3的边 a_{23}=4和从 \color{Green}{2}到 4的边 a_{24}=3,两者取最小值,故 vl(2)=min(vl(3)-a_{23},vl(4)-a_{24})=min(9,10)=9;
- 对于事件 1,仅存在从 \color{Green}{1}到 2的边 a_{12}=5,故 vl(1)=vl(2)-a_{12}=4;
- 对于事件 0,存在从 \color{Green}{0}到 1的边 a_{01}=4和从 \color{Green}{0}到 2的边 a_{02}=6,两者取最小值,故 vl(0)=min(vl(1)-a_{01},vl(2)-a_{02})=min(0,3)=0.
计算事件的最早发生时间和最迟发生时间如下表:
接着计算活动最早开始时间 e()和最迟开始时间 l()(最早看弧起点,最迟看弧终点)
- 对于活动 a_{01},弧起点为 0,弧终点为 1,故 e(a_{01})=ve(0)=0, l(a_{01})=vl(1)-a_{01}=0;
- 对于活动 a_{02},弧起点为 0,弧终点为 2,故 e(a_{02})=ve(0)=0, l(a_{02})=vl(2)-a_{02}=3;
- 对于活动 a_{12},弧起点为 1,弧终点为 2,故 e(a_{12})=ve(1)=4, l(a_{12})=vl(2)-a_{12}=4;
- 对于活动 a_{23},弧起点为 2,弧终点为 3,故 e(a_{23})=ve(2)=9, l(a_{23})=vl(3)-a_{23}=9;
- 对于活动 a_{24},弧起点为 2,弧终点为 4,故 e(a_{24})=ve(2)=9, l(a_{24})=vl(4)-a_{24}=10;
- 对于活动 a_{35},弧起点为 3,弧终点为 5,故 e(a_{35})=ve(3)=13, l(a_{35})=vl(5)-a_{35}=13;
- 对于活动 a_{45},弧起点为 4,弧终点为 5,故 e(a_{45})=ve(4)=12, l(a_{45})=vl(5)-a_{45}=13;
计算活动的最早开始时间和最迟开始时间如下表:
综上,关键路径为 \color{Red}{(0,1,2,3,5)},关键路径长度为 \color{Red}{16}(即事件 5的最早发生时间或最迟发生时间)
6.4.10
2014统考真题:某网络中的路由器运行 OSPF路由协议,下表是路由器 R1维护的主要链路状态信息( LSI), R1构造的网络拓扑图(见下图)是根据题下表及 R1的接口名构造出来的网络拓扑.
请回答下列问题
- 本题中的网络可抽象为数据结构中的哪种逻辑结构?
- 针对表中的内容,设计合理的链式存储结构,以保存表中的链路状态信息( LSI).要求给出链式存储结构的数据类型定义,并画出对应表的链式存储结构示意图(示意图中可仅以 ID标识结点).
- 按照 Dijkstra算法的策略,依次给出 R1到达子网 192.1.x.x的最短路径及费用.
(1) 无向图/网状结构/非线性结构
(2)
typedef struct LinkNode
{
unsigned int ID; //所连路由器的 Router ID
unsigned int IP; //本地 IP 地址
}LinkNode; //Link 的结构
typedef struct NetNode
{
unsighed int Prefix; //IP 前段
unsighed int Mask; //掩码
}NetNode; //Net 的结构
typedef struct ArcNode
{
int Flag; //当 Flag=1 时, 表示 Link; 当 Flag=2 时,表示 Net
union
{
LinkNode Lnode;
NetNode Nnode;
}LinkORNet; //用 union 定义 Link 结点和 Net 结点
unsighed int Metric; //费用
struct ArcNode * Next; //用于指向下一个弧结点的指针
}ArcNode; //弧结点的结构
typedef struct HNode
{
unsighed int RouterID; //路由器的 Router ID
ArcNode * LN_link; //用于指向弧结点的指针
struct HNode * Next; //用于指向下一个表头结点的指针
}HNode; //表头结点的结构
(3) 计算结构如下图所示.
6.4.11
2017统考真题:使用 Prim算法求带权连通图的最小(代价)生成树( MST).请回答下列问题:
- 对下列图 G,从顶点 A开始求 G的 MST,依次给出按算法选出的边.
- 图 G的 MST是唯一的吗?
- 对任意的带权连通图,满足什么条件时,其 MST是唯一的?
![]()
(1)
- 选择 (A,D)
- 选择 (D,E)
- 选择 (C,E)(不能选择 (A,E),会构成环)
- 选择 (B,C)
(2) 图 G的 MST是唯一的.由于该最小生成树包括了图中权值最小的 4条边,其他边除 (A,E)外都比 4条边大,但若用 (A,E)替换同权值的 (C,E), A,D,E三个顶点构成了回路.因此不能替换,所以此图的 MST唯一.
(3) 当带权连通图的任意一个环中所包括的边的权值均不相同时,其 MST是唯一的.
6.4.12
2018统考真题:拟建设一个光通信骨干网络连通 BJ,CS,XA,QD,JN,NJ,TL和 WH等 8个城市.下图中无向边上的权值表示两个城市之间备选光缆的铺设费用.(
猜测
8个城市分别是北京,长沙,西安,青岛,济南,南京,铁岭,武汉
)
- 仅从铺设费用角度出发,给出所有可能的最经济的光缆铺设方案(用带权图表示),并计算相应方案的总费用.
- 该图可采用图的哪种存储结构?给出求解问题 1所用的算法名称.
- 假设每个城市采用一个路由器按 1中得到的最经济方案组网,主机 H1直接连接在 TL的路由器上,主机 H2直接连接在 BJ的路由器上,若 H1向 H2发送一个 TTL=5的 IP分组,则 H2是否可以收到该 IP分组?
![]()
(1) 方案如下,两种方案的总费用均为为 2\times 5 + 3\times 2=16.
(2) 存储结构为邻接矩阵(或邻接表) ,算法名称为 Kruskal算法(或 Prim算法).
(3) IP分组每经过一个路由器 R的转发,其头部中的生存时间 TTL字段的值减 1,当 TTL字段的值减少到 0时路由器会丢弃该 IP分组并给源主机发送时间超过
类型的 ICMP差错报告报文.
-
对于方案 1,主机 H1给 H2发送 IP分组, IP分组的转发路径为 H1 \to R_{TL} \to R_{JN} \to R_{QD} \to R_{WH} \to R_{XA} \to R_{BJ} \to H2, IP分组从 H1发出时其 TTL=5,当该分组转发进入 R_{XA}路由器时,其 TTL=0,该分组会被 R_{XA}路由器丢弃,因此 H2不能收到该 IP分组.
-
对于方案 2,主机 H1给 H2发送 IP分组, IP分组的转发路径为 H1 \to R_{TL} \to R_{BJ} \to H2, IP分组从 H1发出时其 TTL=5,中途只需经过 R_{TL}和 R_{BJ}两个路由器,因此主机 H2可以收到该 IP分组.
tql