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\n"); 31 } 32 else 33 { 34 printf("%d\n",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\n",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:作為一個剛剛接觸的渣渣,我覺得這道題有點難,做了很久,實在沒思路的話就跳過吧