假定一個工程項目由一組子任務構成,子任務之間有的可以並行執行,有的必須在完成了其它一些子任務後才能執行。“任務調度”包括一組子任務、以及每個子任務可以執行所依賴的子任務集。
比如完成一個專業的所有課程學習和畢業設計可以看成一個本科生要完成的一項工程,各門課程可以看成是子任務。有些課程可以同時開設,比如英語和C程序設計,它們沒有必須先修哪門的約束;有些課程則不可以同時開設,因爲它們有先後的依賴關係,比如C程序設計和數據結構兩門課,必須先學習前者。
但是需要注意的是,對一組子任務,並不是任意的任務調度都是一個可行的方案。比如方案中存在“子任務A依賴於子任務B,子任務B依賴於子任務C,子任務C又依賴於子任務A”,那麼這三個任務哪個都不能先執行,這就是一個不可行的方案。
任務調度問題中,如果還給出了完成每個子任務需要的時間,則我們可以算出完成整個工程需要的最短時間。在這些子任務中,有些任務即使推遲幾天完成,也不會影響全局的工期;但是有些任務必須準時完成,否則整個項目的工期就要因此延誤,這種任務就叫“關鍵活動”。
請編寫程序判定一個給定的工程項目的任務調度是否可行;如果該調度方案可行,則計算完成整個工程項目需要的最短時間,並輸出所有的關鍵活動。
輸入格式:
輸入第1行給出兩個正整數NNN(≤100\le 100≤100)和MMM,其中NNN是任務交接點(即銜接相互依賴的兩個子任務的節點,例如:若任務2要在任務1完成後纔開始,則兩任務之間必有一個交接點)的數量。交接點按1~NNN編號,MMM是子任務的數量,依次編號爲1~MMM。隨後MMM行,每行給出了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
參考代碼:
#include <cstdlib>
#include <queue>
#include <iostream>
#include <stack>
using namespace std;
/*
關鍵活動——拓撲排序+最短路徑
鄰接矩陣
*/
#define MaxNum 100
#define INFINITY 65533
struct GNode{
int Nv;
int Ne;
int G[MaxNum][MaxNum];
};
typedef struct GNode *Graph;
/*
記錄Top排序後的結果,其中
change[1]表示任務開始頂點
change[G->Nv]表示任務結束頂點
*/
int change[MaxNum] = {0,};
void INDEGREE(Graph G, int Indegree[])
{
for (int V = 1; V <= G->Nv; V++){
for (int i = 1; i <= G->Nv; i++){
if (G->G[i][V] < INFINITY)
Indegree[V]++;
}
}
}
void KeyActivity(int Ecost[], int Lcost[], Graph G)
{
//以下求最早發生時間
for (int V = 1; V <= G->Nv; V++){
for (int i = 1; i <= G->Nv; i++){
if (G->G[change[V]][i] < INFINITY)
if (Ecost[change[V]] + G->G[change[V]][i] > Ecost[i]){
Ecost[i] = Ecost[change[V]] + G->G[change[V]][i];
}
}
}
//以下求最晚發生時間
for (int i = 1; i <= G->Nv; i++)
Lcost[i] = Ecost[change[G->Nv]];
printf("%d\n", Ecost[change[G->Nv]]);
for (int V = G->Nv; V >= 1; V--){
for (int i = G->Nv; i >= 1; i--){
if (G->G[change[V]][i] < INFINITY)
if (Lcost[i] - G->G[change[V]][i] < Lcost[change[V]]){
Lcost[change[V]] = Lcost[i] - G->G[change[V]][i];
}
}
}
for (int i = 1; i <= G->Nv; i++){
for (int j = 1; j <= G->Nv; j++)
if (G->G[i][j] < INFINITY){
int e = Ecost[i];
int l = Lcost[j] - G->G[i][j];
if ( e == l)
printf("%d->%d\n", i, j);
}
}
}
int TopSort(Graph G, int Indegree[])
//用來判斷任務的調度是否可行
{
int cnt = 0;
int sum = 0;
queue<int> q;
for (int i = 1; i <= G->Nv; i++){
//計算每個頂點的入度
if (Indegree[i] == 0){
q.push(i);
cnt++;
}
}
//int max[MaxNum] = {0,};
int V;
while (!q.empty()){
V = q.front();
q.pop();
sum++;
change[sum] = V;//將拓撲排序結果存入change中
for (int i = 1; i <= G->Nv; i++)
if (G->G[V][i] < INFINITY){//對於V的每個鄰接點
Indegree[i]--;
if (Indegree[i] == 0){
q.push(i);
cnt++;
}
}
}
if (cnt < G->Nv)
return 0;
else
return 1;
}
int main(int argc, char const *argv[])
{
int N, M, time;
scanf("%d %d\n", &N, &M);
int a, b;
Graph G = (Graph)malloc(sizeof(struct GNode));
G->Nv = N;
G->Ne = M;
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++)
G->G[i][j] = INFINITY;
for (int i = 0; i < M; i++){
scanf("%d %d %d", &a, &b, &time);
//有向圖
G->G[a][b] = time;
}
int Indegree[MaxNum] = {0,};
int Ecost[MaxNum] = {0,};
int Lcost[MaxNum] = {0,};
INDEGREE(G, Indegree);
int check = TopSort(G, Indegree);
if (check == 0)
printf("0\n");
else
KeyActivity(Ecost, Lcost, G);
system("pause");
return 0;
}