7-11 关键活动
题目
原题链接
https://pintia.cn/problem-sets/1399202744970727424/problems/1418527362277498881
假定一个工程项目由一组子任务构成,子任务之间有的可以并行执行,有的必须在完成了其它一些子任务后才能执行。“任务调度”包括一组子任务、以及每个子任务可以执行所依赖的子任务集。
比如完成一个专业的所有课程学习和毕业设计可以看成一个本科生要完成的一项工程,各门课程可以看成是子任务。有些课程可以同时开设,比如英语和C程序设计,它们没有必须先修哪门的约束;有些课程则不可以同时开设,因为它们有先后的依赖关系,比如C程序设计和数据结构两门课,必须先学习前者。
但是需要注意的是,对一组子任务,并不是任意的任务调度都是一个可行的方案。比如方案中存在“子任务A依赖于子任务B,子任务B依赖于子任务C,子任务C又依赖于子任务A”,那么这三个任务哪个都不能先执行,这就是一个不可行的方案。
任务调度问题中,如果还给出了完成每个子任务需要的时间,则我们可以算出完成整个工程需要的最短时间。在这些子任务中,有些任务即使推迟几天完成,也不会影响全局的工期;但是有些任务必须准时完成,否则整个项目的工期就要因此延误,这种任务就叫“关键活动”。
请编写程序判定一个给定的工程项目的任务调度是否可行;如果该调度方案可行,则计算完成整个工程项目需要的最短时间,并输出所有的关键活动。
输入格式
输入第1行给出两个正整数N(≤100)和M,其中N是任务交接点(即衔接相互依赖的两个子任务的节点,例如:若任务2要在任务1完成后才开始,则两任务之间必有一个交接点)的数量。交接点按1~N编号,M是子任务的数量,依次编号为1~M。随后M行,每行给出了3个正整数,分别是该任务开始和完成涉及的交接点编号以及该任务所需的时间,整数间用空格分隔。
输出格式
如果任务调度不可行,则输出0;否则第1行输出完成整个工程项目需要的时间,第2行开始输出所有关键活动,每个关键活动占一行,按格式“V->W”输出,其中V和W为该任务开始和完成涉及的交接点编号。关键活动输出的顺序规则是:任务开始的交接点编号小者优先,起点编号相同时,与输入时任务的顺序相反。
输入样例
7 8 |
输出样例
17 |
题目分析
小白在csdn上的第一篇题解,送给这道好不容易弄懂的数构题!!
AOE网
在写这道题之前,先复习一下 AOE网 的相关知识:(大佬自行跳过~)
AOE网,简单来说就是工程的带权有向图,其中:
- 顶点:活动开始或者结束的事件
- 边:活动
- 边的权值:完成该活动所需的时间
在AOE网中,想要完成一项活动,必须要先完成在该活动前面的所有活动,例如下图中,想要完成活动e,必须要先完成活动abcd,完成活动a和c所需时间为3 + 2 = 5,完成活动b和d所需时间为5 + 4 = 9,二者取大,因此任务e的最早开始时间为9。
编辑
由此我们可以知道,整个工程从开始到结束所需要花费的时间是起始点到终止点的最大路径长度(因为这样才可以保证在终止点前的所有任务都完成了),这个有最大路径长度的路径就是关键路径,关键路径上的活动就叫做关键活动。
然后来看题吧!!
题目要求我们输出整个工程项目所需时间和所有关键活动。
Problem 1:求整个工程项目所需时间
首先,题目没有告诉我们从哪个点开始到哪个点结束,所以需要先记录一下每个点的入度和出度,入度为0的点是起始点,出度为0的点就是终止点啦。
然后,用early数组记录每个点(事件)的最早发生时间,假设有事件i和j,它们之间有关系j -> i,那么事件i的最早发生时间early[i] = max(early[i], early[j] + weight),(因为i前面的所有活动全部完成才能开始i,所以在前面的路径中选取最大值),这样到最后,early数组中的最大值就是整个工程的最早完成时间啦。
Problem 2:判断哪些是关键路径上的活动
话不多说举个栗子(还是以上面的图为例吧(才不是懒得画图))——
编辑
结点4的最早发生时间上面说过啦是9,因为需要在活动abcd全部完成之后才能到结点4,换一种说法,就是在9的时间里,需要完成1->2->4和1->3->4这两段:
- 先来看1->3->4这一段,1->3花费时间5,3->4花费时间4,所以想要在9时到达4,必须要在5时到达3,也就是事件3的最晚发生时间是5(如果5时没有到达事件3,那就不可能在9时到达事件4)
- 再来看1->2->4这一段,1->2花费时间3,2->4花费时间2,我们知道只要在9时到达事件4即可,所以【最迟】可以在7时到达事件2(当然比7时早也没什么关系),也就是在7时到达事件2,可以保证我们9时能够到达事件4,从而不耽误整个工程
综上所述,事件2的最早发生时间是3,最晚发生时间是7,事件3的最早发生时间是5,最晚发生时间还是5.
当同一个事件的【最早发生时间】与【最晚发生时间】相等,就证明这个事件就是关键路径上的结点**(很重要!!!)**
在problem1中我们求出了每个结点的最早发生时间early,可以利用early求出每个结点的最晚发生时间late,它们之间的关系是(前提:i->j)事件i的最晚发生时间late[i] = early[j] - weight[i, j]
现有事件 i 和 j (i -> j),当它们满足以下三点:
- i 和 j 之间有边
- i 和 j 的最早发生时间分别等于各自的最晚发生时间
- j的最早发生时间late[j] = i的最早发生时间early[i] + ij之间的边的权值weight[i, j]
就可以判断i->j是一个关键活动啦!!
problem 3:如何按题目要求输出
题目中要求:
关键活动输出的顺序规则是:任务开始的交接点编号小者优先,起点编号相同时,与输入时任务的顺序相反。
因此输出中两层循环:
- 第一层从小到大,保证任务开始的交接点从小到大
- 第二层从大到小,保证起点编号相同情况下,与输入的顺序相反
代码
|