在大型工程的施工前,我們把整個工程劃分為若干個子工程,並把這些子工程編號為1、2、……、N;這樣劃分之後,子工程之間就會有一些依賴關係,即一些子工程必須在某些子工程完成之後才能施工。由於子工程之間有相互依賴關係,因此有兩個任務需要我們去完成:首先,我們需要計算整個工程最少的完成時間;同時,由於一些不可預測的客觀因素會使某些子工程延期,因此我們必須知道哪些子工程的延期會影響整個工程的延期,我們把有這種特徵的子工程稱為關鍵子工程,因此第二個任務就是找出所有的關鍵子工程,以便集中精力管理好這些子工程,盡量避免這些子工程延期,達到用最快的速度完成整個工程。為了便於編程,現在我們假設: (1)根據預算,每一個子工程都有一個完成時間。 (2)子工程之間的依賴關係是:部分子工程必須在一些子工程完成之後才開工。 (3)只要滿足子工程間的依賴關係,在任何時刻可以有任何多個子工程同時在施工,也既同時施工的子工程個數不受限制。 (4)整個工程的完成是指:所有子工程的完成。 例如,有五個子工程的工程規劃表: 序號 完成時間 子工程1 子工程2 子工程3 子工程4 子工程5 子工程1 5 - 0 0 0 0子工程2 4 0 - 0 0 0子工程3 12 0 0 - 0 0子工程4 7 1 1 0 - 0子工程5 2 1 1 1 1 -其中,表格中第 I+1 行 J+2 列的值如為 0 表示“子工程 I”可以在“子工程 J”沒完成前施工,為 1 表示“子工程 I”必須在“子工程 J”完成後才能施工。上述工程最快完成時間為 14 天,其中子工程1、3、4、5為關鍵子工程。 又例如,有五個子工程的工程規劃表: 序號 完成時間 子工程1 子工程2 子工程3 子工程4 子工程5 子工程1 5 - 0 1 0 0子工程2 4 0 - 0 0 0子工程3 12 0 0 - 1 0子工程4 7 1 1 0 - 0 子工程5 2 1 1 1 1 -上述的子工程劃分不合理,因為無法安排子工程1,3,4的施工。 輸入資料: 第 1 行為N,N是子工程的總個數,N≤200。 第2 行為N個正整數,分別代表子工程1、2、……、N的完成時間。 第 3 行到 N+2 行,每行有 N-1 個 0 或 1。其中的第 I+2 行的這些 0,1,分別表示“子工程 I”與子工程1、2、…、I-1、I+1、…N的依賴關係,(I=1、2、……、N)。每行資料之間均用一個空格分開。 輸出資料: 如子工程劃分不合理,則輸出-1; 如子工程劃分合理,則用兩行輸出:第1行為整個工程最少的完成時間。第2行為按由小到大順序輸出所有關鍵子工程的編號。 範例: 輸入檔案名稱:project.in 5 5 4 12 7 2 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 1 1 1 輸出檔案名:project.out 14 1 3 4 5
可用記憶化搜尋或者拓撲排序解決。
狀態:用f[i]表示完成i所需的最小時間及等待時間。
轉移方程:f[i] = max(f[j]) + t[i],其中,j是i的前驅結點。
ACCode:
#include <cstdio>#include <cstring>#include <cstdlib>#include <bitset>using std::bitset;using std::max;const char fi[] = "project.in";const char fo[] = "project.out";const int maxN = 210;struct Edge{int dest; Edge *next; };Edge *edge[maxN];int f[maxN];int t[maxN];int Ou[maxN];bitset <maxN> key;bitset <maxN> marked;int n, ok; void init_file() { freopen(fi, "r", stdin); freopen(fo, "w", stdout); } void insert(int u, int v) { Edge *p = new Edge; p -> dest = v; p -> next = edge[u]; edge[u] = p; } void readdata() { scanf("%d", &n); for (int i = 1; i < n + 1; ++i) scanf("%d", t + i); for (int i = 1; i < n + 1; ++i) for (int j = 1; j < n + 1; ++j) if (i != j) { scanf("%d", &ok); if (ok) {insert(i, j); ++Ou[j]; } } for (int i = 1; i < n + 1; ++i) if (Ou[i] == 0) insert(0, i); } void DP(int u) { if (f[u]) return; //要記憶化。 marked.set(u); for (Edge *p = edge[u]; p; p = p -> next) { int v = p -> dest; if (marked.test(v)) {printf("-1"); exit(0); }//判斷是否存在環。 DP(v); f[u] = max(f[u], f[v]); } f[u] += t[u]; marked.reset(u); //要記得清空標記。 } void Find(int u) { int Max = 0; for (Edge *p = edge[u]; p; p = p -> next) Max = max(Max, f[p -> dest]); for (Edge *p = edge[u]; p; p = p -> next) if (f[p -> dest] == Max) {key.set(p -> dest); Find(p -> dest); } } void work() { DP(0); Find(0); printf("%d\n", f[0]); for (int i = 1; i < n + 1; ++i) if (key.test(i)) printf("%d ", i); } int main(){ init_file(); readdata(); work(); exit(0);}