全部每周作业和视频思考题答案和解析 见 浙江大学 数据结构 思考题+每周练习答案

题目:假定一个工程项目由一组子任务构成,子任务之间有的可以并行执行,有的必须在完成了其它一些子任务后才能执行。“任务调度”包括一组子任务、以及每个子任务可以执行所依赖的子任务集。

比如完成一个专业的所有课程学习和毕业设计可以看成一个本科生要完成的一项工程,各门课程可以看成是子任务。有些课程可以同时开设,比如英语和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
1 2 4
1 3 3
2 4 5
3 4 3
4 5 1
4 6 6
5 7 5
6 7 2

输出样例:

17
1->2
2->4
4->6
6->7

解答:

首先需要注意的是不知道为啥这里的顶点标号又变成了从1到N,而不是上个题的0到N-1,搞这种细节游戏真是麻烦。所以要注意把相应的读入都减1然后存储。

直接把上个题的程序(别忘了读入后减1)拿来,然后改改调度不可行输出0,就可以把第一部分正确输出了。

第二部分是找关键路径,就是再往回遍历了呗。

思路也不难,建立一个反向的流程网络就好了(无非就是把V1和V2点交换一下)。

我们需要修改一下读入,生成一个倒着的图。

然后返回的时候我们需要这么做:

 for (V = 0; V < Graph->Nv; V++)if (Indegree[V] == 0) {myQueue.push(V);Latest[V] = Earliest[V];}else {Latest[V] = MYINFINITY;}。。。。。。。if ((Latest[V] - W->Weight)<Latest[W->AdjV]) {Latest[W->AdjV] = Latest[V] - W->Weight;}

上面是把一个新建的数组进行初始化,注意因为反向是找小的,所以除了0度的点,其他初始化都是无穷。然后在循环遍历的时候判断找出回向中最小的时间。

为了验证我们的计算是否正确,先把图画一画:

然后反向找最小时间:

打印输出和上图完全一致。(因为程序现在太长,所以不发中间过程版本了)

按道理现在应该很简单了,但是题目中有个要求:任务开始的交接点编号小者优先,起点编号相同时,与输入时任务的顺序相反。我写程序的时候产生了点疑问:程序输入没有告诉你输入是从小到大的,也就是说,你可能得进行排序?然后是,起始点一样的话,需要与输入方向相反?为什么呢?

转念一想,原来是这样!因为你输入的顺序与你插入到邻接表的顺序是相同的,但是邻接表是往头上插,所以顺序相反(换句话说,这个题的答案要求不就是为了使用邻接表来做嘛!)

所以需要注意的是当我去浏览别人的解法时,很多人根本没有注意到这个“与输入顺序相反”这个要求,但是因为检测的数据的原因不会看出错误,但是并不是说写的就完全正确,他们是这么写的:

        for(int i = 1; i <= n; i++){.........for(int j = n; j >= 1; j--){
........

但是,你不知道人家输入的顺序就是从小到大输入的呀?题目中没有这么说。反正我看了三四遍题目,也没有这么说。

但是这里牵扯到另外一个问题:如何从最小的起始顶点依次输出

难道要搞一个最小堆?太麻烦了。

这个题真的有些不好,如果是直接按照从i到j依次递增输出,就直接用个邻接矩阵做不就好了?

吐槽完了,来点实际的。既然题目已经这熊样了,我也没有办法,懒得再用邻接矩阵写一遍,直接搞排序。

还有一个需要注意的地方:从后向前遍历的时候,如果有多条路通向多个末尾,我们所有末尾的值要设为最短时间

程序代码:

#include <iostream>
#include <queue>
using namespace std;#define MaxVertexNum 1000
typedef int Vertex;
#define MYINFINITY 60000000
// 邻接表存储 - Kruskal最小生成树算法 //-------------------- 顶点并查集定义 --------------------
typedef Vertex ElementType; // 默认元素可以用非负整数表示
typedef Vertex SetName;     // 默认用根结点的下标作为集合名称
typedef ElementType SetType[MaxVertexNum]; // 假设集合元素下标从0开始
typedef int WeightType;       // 边的权值设为整型
typedef char DataType;        // 顶点存储的数据类型设为字符型
Vertex Earliest[MaxVertexNum];
Vertex Latest[MaxVertexNum];
queue<Vertex> myQueue;
//下面三个数组是为了生成反向AOE图的
Vertex V1Array[MaxVertexNum];
Vertex V2Array[MaxVertexNum];
WeightType WeightArray[MaxVertexNum];
//存储关键活动的
int keyActivityNum = 0;
struct keyActivity {Vertex Vstart;Vertex Vend;
};
keyActivity myKeyActivity[MaxVertexNum];
// 边的定义
typedef struct ENode *PtrToENode;
struct ENode {Vertex V1, V2;      // 有向边<V1, V2> WeightType Weight;  // 权重
};
typedef PtrToENode Edge;
//邻接点的定义
typedef struct AdjVNode *PtrToAdjVNode;
struct AdjVNode {Vertex AdjV;        // 邻接点下标 WeightType Weight;  // 边权重 PtrToAdjVNode Next;    // 指向下一个邻接点的指针
};
//顶点表头结点的定义
typedef struct Vnode {PtrToAdjVNode FirstEdge;  // 边表头指针 DataType Data;             // 存顶点的数据 // 注意:很多情况下,顶点无数据,此时Data可以不用出现
} AdjList[MaxVertexNum];        // AdjList是邻接表类型
//图结点的定义
typedef struct GNode *PtrToGNode;
struct GNode {int Nv;           // 顶点数 int Ne;          // 边数   AdjList G;      // 邻接表
};
typedef PtrToGNode LGraph; // 以邻接表方式存储的图类型 LGraph CreateGraph(int VertexNum)
{ //初始化一个有VertexNum个顶点但没有边的图 Vertex V;LGraph Graph;Graph = (LGraph)malloc(sizeof(struct GNode)); // 建立图 Graph->Nv = VertexNum;Graph->Ne = 0;//初始化邻接表头指针 //注意:这里默认顶点编号从0开始,到(Graph->Nv - 1) for (V = 0; V<Graph->Nv; V++)Graph->G[V].FirstEdge = NULL;return Graph;
}void InsertEdge(LGraph Graph, Edge E)
{PtrToAdjVNode NewNode;//插入边 <V1, V2> //为V2建立新的邻接点 NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));NewNode->AdjV = E->V2;NewNode->Weight = E->Weight;//将V2插入V1的表头 NewNode->Next = Graph->G[E->V1].FirstEdge;Graph->G[E->V1].FirstEdge = NewNode;//注意拓扑排序是用的有向图//若是无向图,还要插入边 <V2, V1> //为V1建立新的邻接点 //NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));//NewNode->AdjV = E->V1;//NewNode->Weight = E->Weight;//将V1插入V2的表头 //NewNode->Next = Graph->G[E->V2].FirstEdge;//Graph->G[E->V2].FirstEdge = NewNode;
}LGraph BuildGraph()
{LGraph Graph;Edge E;Vertex V;int Nv, i;cin >> Nv;   //读入顶点个数 Graph = CreateGraph(Nv); //初始化有Nv个顶点但没有边的图 cin >> Graph->Ne;   //读入边数 if (Graph->Ne != 0) { //如果有边 E = (Edge)malloc(sizeof(struct ENode)); //建立边结点 //读入边,格式为"起点 终点 权重",插入邻接矩阵 for (i = 0; i<Graph->Ne; i++) {cin >> E->V1 >> E->V2 >> E->Weight;E->V1--;E->V2--;V1Array[i] = E->V1;V2Array[i] = E->V2;WeightArray[i] = E->Weight;//注意:如果权重不是整型,Weight的读入格式要改 InsertEdge(Graph, E);}}//如果顶点有数据的话,读入数据 //for (V = 0; V<Graph->Nv; V++)//cin >> Graph->G[V].Data;return Graph;
}LGraph BuildBackGraph(LGraph Graph1)
{LGraph Graph;Edge E;Vertex V;int Nv = Graph1->Nv, i;Graph = CreateGraph(Nv); //初始化有Nv个顶点但没有边的图if (Graph1->Ne != 0) { //如果有边 E = (Edge)malloc(sizeof(struct ENode)); //建立边结点 //读入边,格式为"起点 终点 权重",插入邻接矩阵 for (i = 0; i<Graph1->Ne; i++) {E->V1 = V2Array[i];E->V2 = V1Array[i];E->Weight = WeightArray[i];//注意:如果权重不是整型,Weight的读入格式要改 InsertEdge(Graph, E);}}//如果顶点有数据的话,读入数据 //for (V = 0; V<Graph->Nv; V++)//cin >> Graph->G[V].Data;return Graph;
}//返回最大的元素
Vertex getMaxElement(Vertex arr[],int N) {Vertex maxElement = 0;for (int i = 0; i < N; i++)if (maxElement < arr[i]) {maxElement = arr[i];}return maxElement;
}//邻接表存储 - 拓扑排序算法
bool TopSort(LGraph Graph, Vertex TopOrder[])
{ //对Graph进行拓扑排序,  TopOrder[]顺序存储排序后的顶点下标 int Indegree[MaxVertexNum], cnt;Vertex V;PtrToAdjVNode W;//初始化Indegree[] for (V = 0; V<Graph->Nv; V++)Indegree[V] = 0;//遍历图,得到Indegree[] for (V = 0; V<Graph->Nv; V++)for (W = Graph->G[V].FirstEdge; W; W = W->Next)Indegree[W->AdjV]++; //对有向边<V, W->AdjV>累计终点的入度 //将所有入度为0的顶点入列 for (V = 0; V < Graph->Nv; V++)if (Indegree[V] == 0)myQueue.push(V);//下面进入拓扑排序 cnt = 0;while (!myQueue.empty()) {V = myQueue.front(); //弹出一个入度为0的顶点 myQueue.pop();TopOrder[cnt++] = V; //将之存为结果序列的下一个元素 //对V的每个邻接点W->AdjV for (W = Graph->G[V].FirstEdge; W; W = W->Next) {if (--Indegree[W->AdjV] == 0)//若删除V使得W->AdjV入度为0 myQueue.push(W->AdjV); //则该顶点入列 if ((Earliest[V] + W->Weight)>Earliest[W->AdjV]) {Earliest[W->AdjV] = Earliest[V] + W->Weight;}}} //while结束if (cnt != Graph->Nv)return false; //说明图中有回路, 返回不成功标志 elsereturn true;
}//邻接表存储 - 反向查找时间算法
bool keyRoute(LGraph Graph, int showestTime)
{ //对Graph进行拓扑排序,  TopOrder[]顺序存储排序后的顶点下标 int Indegree[MaxVertexNum];Vertex V;PtrToAdjVNode W;//初始化Indegree[] for (V = 0; V<Graph->Nv; V++)Indegree[V] = 0;//遍历图,得到Indegree[] for (V = 0; V<Graph->Nv; V++)for (W = Graph->G[V].FirstEdge; W; W = W->Next)Indegree[W->AdjV]++; //对有向边<V, W->AdjV>累计终点的入度 //将所有入度为0的顶点入列 for (V = 0; V < Graph->Nv; V++)if (Indegree[V] == 0) {myQueue.push(V);Latest[V] = showestTime;}else {Latest[V] = MYINFINITY;}//下面进入拓扑排序 while (!myQueue.empty()) {V = myQueue.front(); //弹出一个入度为0的顶点 myQueue.pop();for (W = Graph->G[V].FirstEdge; W; W = W->Next) {if (--Indegree[W->AdjV] == 0)//若删除V使得W->AdjV入度为0 myQueue.push(W->AdjV); //则该顶点入列 if ((Latest[V] - W->Weight)<Latest[W->AdjV]) {Latest[W->AdjV] = Latest[V] - W->Weight;}}} //while结束return true;
}//邻接表存储 - 再次正向去寻找的机动时间的算法
void cal4KeyRoute(LGraph Graph)
{ //对Graph进行拓扑排序,  TopOrder[]顺序存储排序后的顶点下标 int Indegree[MaxVertexNum], cnt;Vertex V;PtrToAdjVNode W;//初始化Indegree[] for (V = 0; V<Graph->Nv; V++)Indegree[V] = 0;//遍历图,得到Indegree[] for (V = 0; V<Graph->Nv; V++)for (W = Graph->G[V].FirstEdge; W; W = W->Next)Indegree[W->AdjV]++; //对有向边<V, W->AdjV>累计终点的入度 //将所有入度为0的顶点入列 for (V = 0; V < Graph->Nv; V++)if (Indegree[V] == 0)myQueue.push(V);//下面进入拓扑排序 cnt = 0;while (!myQueue.empty()) {V = myQueue.front(); //弹出一个入度为0的顶点 myQueue.pop();//对V的每个邻接点W->AdjV for (W = Graph->G[V].FirstEdge; W; W = W->Next) {if (--Indegree[W->AdjV] == 0)//若删除V使得W->AdjV入度为0 myQueue.push(W->AdjV); //则该顶点入列 if ((Latest[W->AdjV] - W->Weight)<=Earliest[V]) { //注意不是输出有机动时间的,而是输出没有机动时间的//cout << (V) << "->" << (W->AdjV)<< " "<< W->Weight << endl;myKeyActivity[keyActivityNum].Vstart = (V + 1);myKeyActivity[keyActivityNum].Vend = (W->AdjV + 1);keyActivityNum++;}}} //while结束//cout << endl;
}//只对起始点进行排序,不打乱输入反向的顺序(题目要求的)
void mySort(void) {for(int i = 0;i<keyActivityNum-1;i++)for (int j = 0;j<keyActivityNum-i-1;j++) {if (myKeyActivity[j].Vstart > myKeyActivity[j + 1].Vstart) {Vertex temp;temp = myKeyActivity[j].Vstart;myKeyActivity[j].Vstart = myKeyActivity[j + 1].Vstart;myKeyActivity[j + 1].Vstart = temp;temp = myKeyActivity[j].Vend;myKeyActivity[j].Vend = myKeyActivity[j + 1].Vend;myKeyActivity[j + 1].Vend = temp;}}
}int main(void) {LGraph myGraph = BuildGraph();Vertex TopOrder[MaxVertexNum];bool flag = TopSort(myGraph, TopOrder);Vertex showestTime = getMaxElement(Earliest, myGraph->Nv);if (true == flag) {cout << showestTime << endl;//继续处理LGraph backGraph = BuildBackGraph(myGraph);keyRoute(backGraph, showestTime);//for (int i = 0;i < myGraph->Nv;i++) {//  cout << Latest[i] <<" ";//}cout << endl;cal4KeyRoute(myGraph);mySort();for (int i = 0;i < keyActivityNum;i++) {cout << myKeyActivity[i].Vstart << "->" << myKeyActivity[i].Vend << endl;}}else {cout << "0";}system("pause");return 0;
}

测试结果:

数据结构 图9 关键活动相关推荐

  1. 数据结构 — 图 之 关键路径、关键活动 (文字表述)

    1.AOE网: 边表示活动的网络,边表示活动,顶点表示事件,当该事件发生了,就说明触发该事件发生的所有的活动已经完成.一个工程所需的最短时间 就是 完成从开始顶点到终止顶点的最长路径的长度. 2.关键 ...

  2. [图] AOE网-关键路径|关键活动-原理、手算举例、C语言实现

    文章目录 AOE网 AOE的应用(AOE的相关概念) 原理:求关键活动和关键路径 求ve.vl(顶点) 求ee.el(边) 求关键路径,关键活动 手算举例 C语言实现 AOE网 [有向无环图]活动在边 ...

  3. 数据结构--图(Graph)详解(一)

    数据结构–图(Graph)详解(一) 文章目录 数据结构--图(Graph)详解(一) 一.图的基本概念 1.图的分类 2.弧头和弧尾 3.入度和出度 4.(V1,V2) 和 < V1,V2 & ...

  4. 【数据结构(C语言)】数据结构-图

    图 文章目录 图 一.基本概念 1.图的定义 2.约定符号 3.分类 4.子图 5.路 6.其他术语 7.ADT 二.存储结构 1.邻接矩阵(数组) 2.邻接表 三.基本算法 1.遍历 2.求无向图的 ...

  5. 数据结构——图-基本知识点(第七章)

    目录 1. 图的定义 1.1 各种图定义 1.2 图的顶点与边间关系 1.3 连通图相关术语 1.4 图的定义与术语总结 2. 图的抽象数据类型 3. 图的存储结构 3.1 邻接矩阵 3.2 邻接表 ...

  6. 【数据结构-图】4.拓扑排序和关键路径(注解+原理)

    一.拓扑排序 1.1 基本知识 有向无环图:一个有向图中不存在环,简称DAG图 AOV网:用DAG图表示一个工程,其顶点表示活动,用有向边 <Vi,Vj><V_i, V_j>& ...

  7. 数据结构-----图的拓扑排序和关键路径算法

    部分图片取自:http://www.cnblogs.com/navorse/articles/1893863.html 在介绍拓扑排序和关键路径之前,先引入AOE网络的概念: 该图为一个AOE网,顶点 ...

  8. 数据结构--图(Graph)详解(四)

    数据结构–图(Graph)详解(四) 文章目录 数据结构--图(Graph)详解(四) 一.图中几个NB的算法 1.普里姆算法(Prim算法)求最小生成树 2.克鲁斯卡尔算法(Kruskal算法)求最 ...

  9. 08-图9 关键活动 (30 分

    假定一个工程项目由一组子任务构成,子任务之间有的可以并行执行,有的必须在完成了其它一些子任务后才能执行."任务调度"包括一组子任务.以及每个子任务可以执行所依赖的子任务集. 比如完 ...

最新文章

  1. 20145220韩旭飞《网络对抗》Exp6 信息搜集与漏洞扫描
  2. 空调自控系统基础知识及应用
  3. vue.js开发环境搭建
  4. 重学算法第三期|数据结构与算法001
  5. php mqtt qos,Mqtt Qos 深度解读
  6. 深度学习、图像识别的基本概念
  7. PHPCMS商城:模块_购物车+订单模块(资源合集)
  8. Linux常用命令(1)
  9. 【Python K均值聚类算法】
  10. 周轶璐教授:服务好医生,如何更全面地了解数据、利用数据?
  11. 华为鸿蒙系统适配芯片,华为新平板参数曝光,高通骁龙870芯片加持,首发适配鸿蒙系统...
  12. 【期末复习】计算机网络 谢希仁版(六)应用层
  13. Linux SWAP 交换分区配置说明
  14. 【HAL库】HAL库STM32cubemx快速使用
  15. 正弦值、余弦值和正切值
  16. 陕西省汉中市工信局副局长杨双武一行莅临云畅科技考察调研
  17. 初识安卓--简单计算器(上)
  18. Trinity 一条龙策略
  19. moviepy常用操作-拼接镜头
  20. matlab klobuchar模型,区域似大地水准面精化模型算法的优选

热门文章

  1. 游戏测试-测试用例编写规范
  2. 5 款适用于 Linux 的笔记应用
  3. 用python完成99九九乘法表(简单易懂)
  4. Window8平板键盘中英文字符的问题
  5. 解决虚拟机安装64位系统“此主机支持 Intel VT-x,但 Intel VT-x 处于禁用状态”的问题(一般出现在win7 旗舰版)...
  6. Arcgis地理配准栅格数据
  7. Oracle表明明存在SQL查询数据提示表不存在异常
  8. 2021-07-13 前端学习第一天
  9. (读书笔记)大数据时代
  10. 贝叶斯优化LSTM超参数