假定一个工程项目由一组子任务构成,子任务之间有的可以并行执行,有的必须在完成了其它一些子任务后才能执行。“任务调度”包括一组子任务、以及每个子任务可以执行所依赖的子任务集。
比如完成一个专业的所有课程学习和毕业设计可以看成一个本科生要完成的一项工程,各门课程可以看成是子任务。有些课程可以同时开设,比如英语和C程序设计,它们没有必须先修哪门的约束;有些课程则不可以同时开设,因为它们有先后的依赖关系,比如C程序设计和数据结构两门课,必须先学习前者。
但是需要注意的是,对一组子任务,并不是任意的任务调度都是一个可行的方案。比如方案中存在“子任务A依赖于子任务B,子任务B依赖于子任务C,子任务C又依赖于子任务A”,那么这三个任务哪个都不能先执行,这就是一个不可行的方案。
任务调度问题中,如果还给出了完成每个子任务需要的时间,则我们可以算出完成整个工程需要的最短时间。在这些子任务中,有些任务即使推迟几天完成,也不会影响全局的工期;但是有些任务必须准时完成,否则整个项目的工期就要因此延误,这种任务就叫“关键活动”。
输入格式:
输入第1行给出两个正整数N ≤100和M,其中N是任务交接点(即衔接相互依赖的两个子任务的节点,例如:若任务2要在任务1完成后才开始,则两任务之间必有一个交接点)的数量。交接点按1~N编号,M是子任务的数量,依次编号为1~M。随后M行,每行给出了3个正整数,分别是该任务开始和完成涉及的交接点编号以及该任务所需的时间,整数间用空格分隔。
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
输出格式:
如果任务调度不可行,则输出0;否则第1行输出完成整个工程项目需要的时间,第2行开始输出所有关键活动,每个关键活动占一行,按格式“V->W”输出,其中V和W为该任务开始和完成涉及的交接点编号。关键活动输出的顺序规则是:任务开始的交接点编号小者优先,起点编号相同时,与输入时任务的顺序相反。
17
1->2
2->4
4->6
6->7
定义交接点vi的最早发生时间为从开始点到vi点的最长路径长度,记为e(i),也即所有以vi为尾(弧的初始点,弧由尾指向头)的活动的最早开始时间;再定义l(i)为活动ai最迟开始事件,表示在不推迟工程终止点的前提下,活动最迟必须开始进行的时间。l(i)-e(i)即活动ai的时间余量,若为0即为关键活动。为了求e(i)与l(i),先求交接点vi的最早发生时间ve(i)和交接点的最迟发生时间vl(i)。设活动ai为弧<j, k>,持续时间为dut(<j,k>)则:e(i) = ve(i), l(i) = vl(k) - dut(<j, k>)。
而ve(i)和vl(i)分别按照拓扑序和逆拓扑序遍历交接点找到从开始点(时刻为0)到vi点的最长路径即可。逆拓扑序可以通过用栈记录拓扑序遍历中顶点访问顺序得出,然后同时求出时间余量并判断。
分析摘自:数据结构:C语言版/严蔚敏,吴伟民编著 清华大学出版社 P183~184
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 #define OK 1 6 #define ERROR 0 7 8 #define MAX_VEX_NUM 102 9 10 typedef int VexType; 11 typedef int VRType; 12 13 typedef int Status; 14 15 typedef struct ArcNode { 16 VexType adjvex; 17 VRType w; 18 struct ArcNode *next; 19 } ArcNode; 20 typedef struct VNode { 21 // VexType vex; 22 ArcNode *firstarc; 23 } VNode, AdjList[MAX_VEX_NUM]; 24 typedef struct { 25 AdjList vertices; // 邻接表 26 int vexnum, arcnum; 27 } ALGraph; 28 29 void AddArc(ALGraph *pg, int u, int v, int w) { 30 ArcNode *narc = (ArcNode*)malloc(sizeof(ArcNode)); 31 narc->adjvex = v; narc->w = w; narc->next = pg->vertices[u].firstarc; //..... 32 pg->vertices[u].firstarc = narc; 33 } 34 void InputALGraph(ALGraph *pg) { 35 scanf("%d%d", &pg->vexnum, &pg->arcnum); 36 for (int i = 1; i <= pg->vexnum; i++) pg->vertices[i].firstarc = NULL; 37 // memset(pg->vertices, 0, sizeof((pg->vexnum+1) * sizeof(VNode))); 38 int u, v, w; 39 for (int i = 0; i < pg->arcnum; i++) { 40 scanf("%d%d%d", &u, &v, &w); 41 AddArc(pg, u, v, w); 42 } 43 } 44 void FindInDegree(ALGraph *pg, int indegree[]) { 45 for (int i = 1; i <= pg->vexnum; i++) { 46 for (ArcNode *pa = pg->vertices[i].firstarc; pa != NULL; pa = pa->next) 47 indegree[pa->adjvex]++; 48 } 49 } 50 51 #define MAX_STACK_INIT MAX_VEX_NUM 52 53 typedef VexType ElemType; 54 typedef struct { 55 int top, size; 56 ElemType *base; 57 } Stack; 58 void InitStack(Stack *ps) { 59 ps->base = (ElemType*)malloc(MAX_STACK_INIT * sizeof(ElemType)); 60 ps->top = 0; ps->size = MAX_STACK_INIT; 61 } 62 int StackEmpty(Stack *ps) { 63 return ps->top == 0; 64 } 65 void StackPush(Stack *ps, ElemType e) { 66 if (ps->top >= ps->size) {exit(-1);} 67 68 ps->base[ps->top++] = e; 69 } 70 void StackPop(Stack *ps, ElemType *pe) { 71 if (ps->top <= 0) {exit(-2);} 72 73 *pe = ps->base[--ps->top]; 74 } 75 void StackTop(Stack *ps, ElemType *pe) { 76 if (ps->top <= 0) {exit(-2);} 77 78 *pe = ps->base[ps->top-1]; 79 } 80 void StackPrint(Stack *ps) { 81 for (int i = 0; i < ps->top; i++) 82 printf("%d ", ps->base[i]); 83 printf("\n"); 84 } 85 86 Status TopologicalOrderVE(ALGraph *pg, Stack *pt, int ve[MAX_VEX_NUM]) { 87 int cnt = 0, indegree[MAX_VEX_NUM+1] = {}; 88 memset(ve, 0, (pg->vexnum+1)*sizeof(int)); // 89 FindInDegree(pg, indegree); 90 Stack s; InitStack(&s); 91 for (int i = 1; i <= pg->vexnum; i++) 92 if (!indegree[i]) StackPush(&s, i); 93 while (!StackEmpty(&s)) { 94 int j; 95 StackPop(&s, &j); StackPush(pt, j); cnt++; 96 for (ArcNode *pa = pg->vertices[j].firstarc; pa != NULL; pa = pa->next) { 97 int k = pa->adjvex; 98 if (--indegree[k] == 0) StackPush(&s, k); 99 100 if (ve[j]+pa->w > ve[k]) ve[k] = ve[j]+pa->w; // max 101 } 102 } 103 if (cnt < pg->vexnum) return ERROR; 104 else return OK; 105 } 106 Status CriticalPath(ALGraph *pg) { 107 Stack T; InitStack(&T); 108 int ve[MAX_VEX_NUM] = {}, vl[MAX_VEX_NUM] = {}; 109 if (!TopologicalOrderVE(pg, &T, ve)) return ERROR; 110 int maxvex; StackTop(&T, &maxvex); // 不一定是最大的,可能多条路径 111 for (int i = 1; i <= pg->vexnum; i++) 112 if (ve[i] > ve[maxvex]) maxvex = i; 113 printf("%d", ve[maxvex]); 114 115 for (int i = 0; i <= pg->vexnum; i++) vl[i] = ve[maxvex]; 116 while (!StackEmpty(&T)) { 117 int j; ArcNode* pa; 118 for (StackPop(&T, &j), pa = pg->vertices[j].firstarc; pa != NULL; pa = pa->next) { 119 int k = pa->adjvex, dut = pa->w; 120 if (vl[k] - dut < vl[j]) vl[j] = vl[k] - dut; // min 允许的最晚 121 } 122 } 123 for (int j = 1; j < pg->vexnum; j++) 124 for (ArcNode* pa = pg->vertices[j].firstarc; pa != NULL; pa = pa->next) { 125 int k = pa->adjvex, dut = pa->w; 126 int e = ve[j], l = vl[k]-dut; 127 if (e == l) printf("\n%d->%d", j, k); 128 } 129 return OK; 130 } 131 132 int main() { 133 ALGraph g; 134 InputALGraph(&g); 135 if (!CriticalPath(&g)) printf("0"); 136 return 0; 137 }
遇到的问题:逆拓扑序遍历前,找终止点时,没有考虑到可能存在多条关键路径,多个结尾的情况,而没有找到ve最大的点。