7-11 关键活动(30 分)
假定一个工程项目由一组子任务构成,子任务之间有的可以并行执行,有的必须在完成了其它一些子任务后才能执行。“任务调度”包括一组子任务、以及每个子任务可以执行所依赖的子任务集。
比如完成一个专业的所有课程学习和毕业设计可以看成一个本科生要完成的一项工程,各门课程可以看成是子任务。有些课程可以同时开设,比如英语和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;一个最迟发生时间,逆向循环
1 early[i] = FindMax( early[i],early[temp]+G[temp][i] ); 2 3 late[i] = FindMin( late[i],late[temp]-G[i][temp] );
这里要注意的是最早发生时间是求几个路线的最大数,最迟发生时间是求最小数
1 #include<stdio.h> 2 #include<stdlib.h> 3 4 #define MAXVER 105 5 #define INFINITY 65535 6 7 int G[MAXVER][MAXVER]; //图 8 int early[MAXVER]; //最早发生时间 9 int late[MAXVER]; //最迟发生时间 10 int in[MAXVER]; //入度 11 int out[MAXVER]; //出度 12 int nv,ne; //顶点数目 ,边数目 13 14 void CreatGraph(); 15 int EarlyTime(); 16 void LateTime(int Scost); 17 int FindMax( int a,int b); 18 int FindMin( int a,int b); 19 20 int main() 21 { 22 int flag; 23 int i,j; 24 scanf("%d %d",&nv,&ne); 25 26 CreatGraph(); 27 flag = EarlyTime(); 28 if( flag==-1) 29 { 30 printf("0 "); 31 } 32 else 33 { 34 printf("%d ",flag); 35 LateTime( flag ); 36 for( i=1; i<=nv; i++) 37 { 38 if(early[i] != late[i]) 39 continue; 40 for( j=nv; j>=1 ; j--) 41 { 42 if( G[i][j]>=0 && early[j]==late[j] &&late[j]-G[i][j]==early[i]) 43 { 44 //i,j均在关键路径上且相邻 45 printf("%d->%d ",i,j); 46 } 47 } 48 } 49 50 } 51 return 0; 52 } 53 54 void CreatGraph() 55 { 56 int i,j; 57 int s,d,cost; 58 59 for( i=1; i<=nv; i++) 60 { 61 for( j=1; j<=nv; j++) 62 { 63 G[i][j] = -1; 64 } 65 early[i] = 0; 66 late[i] = INFINITY; 67 in[i] = 0; 68 out[i] = 0; 69 } 70 for( i=0; i<ne; i++) 71 { 72 scanf("%d %d %d",&s,&d,&cost); 73 G[s][d] = cost; //有向边 74 in[d] ++; 75 out[s]++; 76 } 77 78 } 79 80 int EarlyTime() 81 { 82 int queue[nv]; 83 int first =-1,rear = -1; 84 int count=0; 85 int i; 86 int temp,ret=0; 87 88 for( i=1; i<=nv; i++) 89 { 90 if( in[i]==0) 91 { 92 //如果入度为0则入队 93 queue[++rear] = i; 94 } 95 } 96 97 while( first<rear) //判断队是否为空 98 { 99 temp = queue[++first]; //出队 100 count++; 101 for( i=1; i<=nv; i++) 102 { 103 if( G[temp][i]>=0 ) 104 { 105 in[i]--; 106 early[i] = FindMax( early[i],early[temp]+G[temp][i]); 107 if( in[i]==0) 108 { 109 queue[++rear] = i; 110 } 111 } 112 } 113 } 114 if( count!=nv) 115 { 116 ret = -1; 117 } 118 else 119 { 120 ret = early[1]; 121 for( i=2; i<=nv; i++) 122 { 123 if(early[i] > ret) 124 { 125 //找出最大的early[i] 126 ret = early[i]; 127 } 128 } 129 } 130 131 return ret; 132 } 133 134 void LateTime(int Scost) 135 { 136 int i; 137 int queue[MAXVER]; 138 int first=-1,rear=-1; 139 int temp; 140 141 for( i=1; i<=nv; i++) 142 { 143 if( out[i]==0) 144 { 145 queue[++rear] = i; 146 late[i] = Scost; 147 } 148 } 149 150 while( first<rear ) 151 { 152 temp = queue[++first]; 153 for( i=nv; i>=1; i--) 154 { 155 if( G[i][temp]>=0) 156 { 157 late[i] = FindMin( late[i],late[temp]-G[i][temp]); 158 out[i]--; 159 if(out[i]==0) 160 { 161 queue[++rear] = i; 162 } 163 } 164 } 165 166 } 167 168 } 169 int FindMax( int a,int b) 170 { 171 if( a>b ) 172 { 173 return a; 174 } 175 else 176 { 177 return b; 178 } 179 } 180 int FindMin( int a,int b) 181 { 182 if( a>b ) 183 { 184 return b; 185 } 186 else 187 { 188 return a; 189 } 190 }
PS:作为一个刚刚接触的渣渣,我觉得这道题有点难,做了很久,实在没思路的话就跳过吧