1. 程式人生 > >7-1 關鍵活動 (30 分)

7-1 關鍵活動 (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

 

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
int a[101][101],in[101]={0},put[101]={0};//記錄入度和出度
int EAT[101]={0},LFT[101];//最早完成時間,最遲完成時間
int main()
{
    queue<int>q;
    stack<int>s,tm;
    int n,m,b,c,d;;
    cin>>n>>m;
    int cnt=0,sum=0;
    for(int i=1;i<=n;i++){//初始化
        for(int j=1;j<=n;j++)
            if(i!=j)a[i][j]=-1;
        LFT[i]=INF;
    }
    for(int i=1;i<=m;i++){
        cin>>b>>c>>d;
        a[b][c]=d;
        in[c]++;
        put[b]++;
    }
    for(int i=1;i<=n;i++){
        if(!in[i]){//找到所有開始點
            q.push(i);
            cnt++;
        }
    }
    while(!q.empty()){//計算各專案的最早開始時間
        int temp=q.front();
        q.pop();
        for(int i=1;i<=n;i++){
            if(a[temp][i]>0){
                if(--in[i]==0){
                    q.push(i);
                    cnt++;
                }
                if(a[temp][i]+EAT[temp]>EAT[i])EAT[i]=a[temp][i]+EAT[temp];
            }
        }
    }
    if(cnt<n)cout<<0<<endl;//不可行
    else {
        int pos,mmax=0;
        for(int i=1;i<=n;i++){//尋找關鍵路徑的最後的專案
            if(EAT[i]>mmax){
                mmax=EAT[i];
                pos=i;
            }
        }
        LFT[pos]=EAT[pos];//最早開始時間與最遲開始時間相同
        for(int i=n;i>=1;i--){//尋找專案終點
            if(!put[i]){
                q.push(i);
            }
        }
        q.push(pos);
        sum=LFT[pos];
        while(!q.empty()){//從後向前計算各專案最遲完成時間
            int temp=q.front();
            q.pop();
            for(int i=1;i<=n;i++){
                if(a[i][temp]>0){
                    q.push(i);
                    if(LFT[i]>LFT[temp]-a[i][temp])LFT[i]=LFT[temp]-a[i][temp];
                }
            }
        }
        cout<<sum<<endl;
        tm.push(pos);
        s.push(pos);
        int vis[101]={0};//輔助陣列
        while(!tm.empty()){//得到關鍵路徑
            int temp=tm.top();
            tm.pop();
            for(int i=n;i>=1;i--){
                if(a[i][temp]>0&&EAT[i]==LFT[i]&&!vis[i]){
                    tm.push(i);
                    vis[i]=1;
                    s.push(i);
                }
            }
        }
        int tt[101],pp=0;
        while(!s.empty()){//為排序準備
            tt[pp++]=s.top();
            s.pop();
        }
        sort(tt,tt+pp,greater<int>());
        for(int i=0;i<pp;i++){
            s.push(tt[i]);//排好序重新入棧
        }
        while(!s.empty()){//輸出
            int temp=s.top();
            s.pop();
            for(int i=n;i>=1;i--){
                if(a[temp][i]>0&&EAT[i]==LFT[i]&&EAT[temp]+a[temp][i]==EAT[i]){//按照路徑輸出
                    cout<<temp<<"->"<<i<<endl;
                }
            }
        }
    }
    return 0;
}