1. 程式人生 > >洛谷Oj-P1113 雜務-拓撲排序

洛谷Oj-P1113 雜務-拓撲排序

問題描述:
John的農場在給奶牛擠奶前有很多雜務要完成,每一項雜務都需要一定的時間來完成它。比如:他們要將奶牛集合起來,將他們趕進牛棚,為奶牛清洗乳房以及一些其它工作。儘早將所有雜務完成是必要的,因為這樣才有更多時間擠出更多的牛奶。當然,有些雜務必須在另一些雜務完成的情況下才能進行。比如:只有將奶牛趕進牛棚才能開始為它清洗乳房,還有在未給奶牛清洗乳房之前不能擠奶。我們把這些工作稱為完成本項工作的準備工作。至少有一項雜務不要求有準備工作,這個可以最早著手完成的工作,標記為雜務1。John有需要完成的n個雜務的清單,並且這份清單是有一定順序的,雜務k(k>1)的準備工作只可能在雜務1..k-1中。
寫一個程式從1到n讀入每個雜務的工作說明。計算出所有雜務都被完成的最短時間。當然互相沒有關係的雜務可以同時工作,並且,你可以假定John的農場有足夠多的工人來同時完成任意多項任務。
AC程式碼:

struct edge//鏈式前向星
{
    int next;
    int to;
};
edge e[1000010];
int head[10010];
int cnt = 1;
int n;//頂點數
int t[10010];//點權,完成該項工作需要花費的時間
int in_degree[10010];//入度
int book[10010];//標記
int dp[10010];//遞推陣列
int tot;
vector<int> prev[10010];//前驅頂點陣列
void add_edge(int u,int v)
{
    e[cnt].to = v;
    e[cnt].next = head[u];
    head[u] = cnt;
    cnt++;
}
bool
stop()//是否應該退出迴圈(while(true)),比較笨的方法 { for(int i = 1; i <= n; ++i) if(book[i] == 0) return false; return true;//如果頂點都被標記了,就退出迴圈 } int main() { cin >> n; memset(head,-1,sizeof(head));//初始化head陣列 for(int i = 1; i <= n; ++i) { int a,b,c; cin
>> a >> b; t[i] = b;//賦點權 while(cin >> c) { if(c == 0) break; add_edge(c,a);//加邊 in_degree[a]++;//邊的終點入度加1 prev[a].push_back(c);//將前驅結點加入陣列 } } for(int i = 1; i <= n; ++i)//初始化dp陣列 dp[i] = t[i]; vector<int> v;//儲存應該刪去的頂點 int state = 0;//當前是第幾階段 while(true) { state++;//當前是第state階段 for(int i = 1; i <= n; ++i)//遍歷每一個頂點 { //從中找出入度為0且沒被標記的頂點 if(in_degree[i] == 0 && book[i] == 0) { book[i] = state;//頂點的階段賦為state if(state >= 2) { for(int j = 0; j <= prev[i].size() - 1; ++j) { //如果頂點i是第2階段及以後的頂點 //且頂點j是第state - 1階段的頂點(誤) //也就是說j是i的上一個階段(誤) //j很有可能與i有邊相連 //if(book[j] == state - 1)//不必作此要求 //if(book[j] <= state - 1) //{ for(int k = head[prev[i][j]]; k != -1; k = e[k].next) { int to = e[k].to; dp[i] = max(dp[i],dp[prev[i][j]] + t[i]);//更新 } //} } } v.push_back(i);//將i存入陣列 } } //刪點 for(int i = 0; i <= v.size() - 1; ++i) { tot++;//計數 for(int j = head[v[i]]; j != -1; j = e[j].next) { int to = e[j].to; in_degree[to]--;//終點的入度-1 } } v.clear(); if(tot == n) break; //if(stop() == true) //break; } int max_t = -inf; for(int i = 1; i <= n; ++i) max_t = max(max_t,dp[i]); cout << max_t << endl; return 0; }

解決方法:
這是一道比較裸的拓撲排序題,所有雜務都被完成的最短時間即是AOE網中的關鍵路徑上的關鍵事件所花時間。如果事件A在事件B之前發生,就從A出發向B連一條邊。建好圖後,我們只需要借鑑數字三角形這一題的思想,通過遞推來找出關鍵路徑。
優化:①stop()->tot == n②for()->prev