1.题目
假定一个工程项目由一组子任务构成,子任务之间有的可以并行执行,有的必须在完成了其它一些子任务后才能执行。“任务调度”包括一组子任务、以及每个子任务可以执行所依赖的子任务集。
比如完成一个专业的所有课程学习和毕业设计可以看成一个本科生要完成的一项工程,各门课程可以看成是子任务。有些课程可以同时开设,比如英语和C程序设计,它们没有必须先修哪门的约束;有些课程则不可以同时开设,因为它们有先后的依赖关系,比如C程序设计和数据结构两门课,必须先学习前者。
但是需要注意的是,对一组子任务,并不是任意的任务调度都是一个可行的方案。比如方案中存在“子任务A依赖于子任务B,子任务B依赖于子任务C,子任务C又依赖于子任务A”,那么这三个任务哪个都不能先执行,这就是一个不可行的方案。
任务调度问题中,如果还给出了完成每个子任务需要的时间,则我们可以算出完成整个工程需要的最短时间。在这些子任务中,有些任务即使推迟几天完成,也不会影响全局的工期;但是有些任务必须准时完成,否则整个项目的工期就要因此延误,这种任务就叫“关键活动”。
请编写程序判定一个给定的工程项目的任务调度是否可行;如果该调度方案可行,则计算完成整个工程项目需要的最短时间,并输出所有的关键活动。
输入格式:
输入第1行给出两个正整数N(≤)和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
2.思路
-数据存储
①输出格式:任务开始的交接点编号小者优先,起点编号相同时,与输入时任务的顺序相反
图用邻接表存储最合适,遍历时按照顶点和边顺序遍历,又因为邻接点插入时是后输入的插入到前面,所以满足后输入先输出的要求。
②需要额外用一个矩阵存储逆向的边
-主要思路
①拓扑排序统计出最快工期
②利用拓扑逆向排序(利用OutDegree数组和存储反向边的矩阵)算出每一个节点的最晚完成时间。
③计算没一条边的松弛时间并输出。
-关键函数
topSort && bottomSort
3.参考代码
#include <stdlib.h> #include <cstdio> #include <queue> #define MaxVertexNum 102 #define INFINITY 65536 using namespace std; typedef int Vertex; typedef int WeightType; typedef int DataType; int Earliest[MaxVertexNum]; int Latest[MaxVertexNum]; int KeyEdge[MaxVertexNum][MaxVertexNum]; typedef struct ENode *Edge; struct ENode{ Vertex V1, V2; WeightType Weight; }; typedef struct AdjVNode *PtrToAdjVNode; struct AdjVNode{ Vertex AdjV; WeightType Weight; PtrToAdjVNode Next; int flag; }; typedef struct Vnode{ PtrToAdjVNode FirstEdge; } AdjList[MaxVertexNum]; /* AdjListÊÇÁÚ½Ó±íÀàÐÍ */ typedef struct GNode *LGraph; struct GNode{ int Nv; int Ne; AdjList G; WeightType Matrix[MaxVertexNum][MaxVertexNum]; }; LGraph CreateGraph( int VertexNum ); void InsertEdge( LGraph Graph, Edge E ); LGraph BuildGraph(); bool topSort(LGraph Graph); int findEarliest(LGraph Graph); void bottomSort(LGraph Graph, int EarilestTime); void outPut(LGraph Graph, int DDL); int main() { LGraph Graph = BuildGraph(); if( !topSort(Graph) ) printf("0\n"); else { int DDL = findEarliest(Graph); bottomSort(Graph, DDL); outPut(Graph, DDL); } } LGraph CreateGraph( int VertexNum ) { Vertex V, W; LGraph Graph; Graph = (LGraph)malloc( sizeof(struct GNode) ); Graph->Nv = VertexNum; Graph->Ne = 0; for (V=0; V<Graph->Nv; V++) Graph->G[V].FirstEdge = NULL; for (V = 0; V<Graph->Nv; V++){ for (W = 0; W<Graph->Nv; W++) { Graph->Matrix[V][W] = INFINITY; KeyEdge[V][W] = 1; } } return Graph; } void InsertEdge( LGraph Graph, Edge E ) { PtrToAdjVNode newNode = new AdjVNode; newNode->Weight = E->Weight; newNode->AdjV = E->V2; newNode->Next = Graph->G[E->V1].FirstEdge; Graph->G[E->V1].FirstEdge = newNode; } LGraph BuildGraph() { LGraph Graph; int Nv; Edge E; Vertex V; scanf("%d", &Nv); Graph = CreateGraph(Nv); scanf("%d", &(Graph->Ne)); if(Graph->Ne) { E = new ENode; for(int i=0; i<Graph->Ne; i++) { scanf("%d %d %d", &(E->V1), &(E->V2), &(E->Weight)); E->V1--; E->V2--; Graph->Matrix[E->V2][E->V1] = E->Weight; InsertEdge(Graph, E); } } return Graph; } bool topSort(LGraph Graph) { int cnt; int Indegree[MaxVertexNum]; Vertex V; queue<Vertex> Q; cnt = 0; for( V=0; V<Graph->Nv; V++) {Indegree[V] = 0; Earliest[V] = 0; } for( V = 0; V < Graph->Nv; V++) { for(PtrToAdjVNode W = Graph->G[V].FirstEdge; W; W=W->Next) { Indegree[W->AdjV]++; } } for(V=0; V<Graph->Nv; V++) if(Indegree[V] == 0) Q.push(V); while(!Q.empty()) { V = Q.front(); Q.pop(); cnt++; for(PtrToAdjVNode W = Graph->G[V].FirstEdge; W; W=W->Next) { if(--Indegree[W->AdjV] == 0) Q.push(W->AdjV); if(Earliest[W->AdjV] < W->Weight + Earliest[V]) { Earliest[W->AdjV] = W->Weight + Earliest[V]; } } } if(cnt != Graph->Nv) return false; return true; } int findEarliest(LGraph Graph) { Vertex V, MaxV; int compTime; compTime = Earliest[0]; for(V = 1; V < Graph->Nv; V++) { if(Earliest[V] > compTime) {compTime = Earliest[V]; MaxV = V; } } return compTime; } void bottomSort(LGraph Graph, int earlyTime) { int OutDegree2[MaxVertexNum]; int cnt; Vertex V; queue<Vertex> Q; cnt = 0; for (V = 0; V < Graph->Nv; V++) { OutDegree2[V] = 0; Latest[V] = INFINITY; } for( V = 0; V < Graph->Nv; V++) { for(PtrToAdjVNode W = Graph->G[V].FirstEdge; W; W=W->Next) { OutDegree2[V]++; } if (!OutDegree2[V]) { Latest[V] = earlyTime; } } for(V=0; V<Graph->Nv; V++) if(OutDegree2[V] == 0) Q.push(V); while(!Q.empty()) { V = Q.front(); Q.pop(); cnt++; for (Vertex W = 0; W<Graph->Nv; W++) { if(Graph->Matrix[V][W] != INFINITY) { if (--OutDegree2[W] == 0) { Q.push(W); } if (Latest[V] - Graph->Matrix[V][W] < Latest[W]) { Latest[W] = Latest[V] - Graph->Matrix[V][W]; } } } } for (Vertex V = 0; V<Graph->Nv; V++) { for (Vertex W = 0; W < Graph->Nv; W++) { KeyEdge[V][W] = Latest[W] - Earliest[V] - Graph->Matrix[W][V]; } } } void outPut(LGraph Graph, int DDL) { printf("%d\n", DDL); for (Vertex V = 0; V<Graph->Nv; V++) { for (PtrToAdjVNode W = Graph->G[V].FirstEdge; W; W=W->Next) { if (KeyEdge[V][W->AdjV] == 0) { printf("%d->%d\n", V+1, W->AdjV+1); } } } }