題目描述
John的農場在給奶牛擠奶前有很多雜務要完成,每一項雜務都需要一定的時間來完成它。比如:他們要將奶牛集合起來,將他們趕進牛棚,爲奶牛清洗乳房以及一些其它工作。儘早將所有雜務完成是必要的,因爲這樣纔有更多時間擠出更多的牛奶。當然,有些雜務必須在另一些雜務完成的情況下才能進行。比如:只有將奶牛趕進牛棚才能開始爲它清洗乳房,還有在未給奶牛清洗乳房之前不能擠奶。我們把這些工作稱爲完成本項工作的準備工作。至少有一項雜務不要求有準備工作,這個可以最早着手完成的工作,標記爲雜務111。John有需要完成的nnn個雜務的清單,並且這份清單是有一定順序的,雜務k(k>1)k(k>1)k(k>1)的準備工作只可能在雜務111至k−1k-1k−1中。
寫一個程序從111到nnn讀入每個雜務的工作說明。計算出所有雜務都被完成的最短時間。當然互相沒有關係的雜務可以同時工作,並且,你可以假定John的農場有足夠多的工人來同時完成任意多項任務。
輸入格式
第1行:一個整數nnn,必須完成的雜務的數目(3≤n≤10,0003 \le n \le 10,0003≤n≤10,000);
第222至(n+1)(n+1)(n+1)行: 共有nnn行,每行有一些用111個空格隔開的整數,分別表示:
-
工作序號(111至nnn,在輸入文件中是有序的);
-
完成工作所需要的時間len(1≤len≤100)len(1 \le len \le 100)len(1≤len≤100);
-
一些必須完成的準備工作,總數不超過100100100個,由一個數字000結束。有些雜務沒有需要準備的工作只描述一個單獨的000,整個輸入文件中不會出現多餘的空格。
輸出格式
一個整數,表示完成所有雜務所需的最短時間。
輸入輸出樣例
輸入 #1
7
1 5 0
2 2 1 0
3 3 2 0
4 6 1 0
5 1 2 4 0
6 8 2 4 0
7 4 3 5 6 0
輸出 #1
23
思路:
問的是完成的最短時間,但卻是實實在在的DAG最長路題,要求所有任務完成的最短時間,就要讓最晚結束的任務儘可能早開始處理,那麼當最晚結束的任務的所有的前驅節點最晚結束的時刻就是最晚結束的任務開始的時刻
這句話很關鍵(減少很多麻煩):
雜務k(k>1)的準備工作只可能在雜務1至k−1中。
代碼一(拓撲排序):
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <set>
#include <map>
using namespace std;
const int maxn=10100;
vector<int> g[maxn];
int n,a,t[maxn],x,ind[maxn],ans=0,endtime[maxn];
//ind數組記錄入度數,endtime記錄每個任務的最優結束時間
int topo()
{
queue<int> q;
for(int i=1;i<=n;i++)
if(ind[i]==0) //沒有前驅,可以入隊
{
q.push(i);
endtime[i]=t[i]; //此情況直接更新結束時間
}
while(q.size())
{
int u = q.front();
q.pop();
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i]; //取出u的後繼v
ind[v]--; //因爲v的前驅u任務結束了,所以v的入度-1
//endtime[v] = max(endtime[v] , endtime[u]+time[v]);
//用max過不了,編譯有問題,不知道爲啥
endtime[v] = endtime[v] > endtime[u]+t[v] ? endtime[v] : endtime[u]+t[v];
//用u來更新後繼節點v的結束時間(此時不一定最優)
//最優的應該是v的最晚結束的前驅才能更新到
if(ind[v]==0)//沒有入度,可以入隊
q.push(v);
}
}
int ans=0;
for(int i=1;i<=n;i++)
//ans = max(ans,endtime[i]); 同上
//經過拓撲排序,所有任務的最晚結束時間都確定了
//因此只需要找到最晚的那個任務的結束時間就是ans
ans = ans>endtime[i]?ans:endtime[i];
return ans;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a>>t[i];
while(cin>>x && x)
{
g[x].push_back(i); //想做i 先做x,所以i是x的後繼
ind[i]++; //i的入度+1
}
}
cout<<topo();
return 0;
}
代碼二(模擬):
這份代碼是從題解裏別人的思路得到的(很簡潔):
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <set>
#include <map>
using namespace std;
const int maxn=10100;
int n,t,a[maxn],maxans=0,maxt=0,x;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
maxt=0;
cin>>i>>a[i];
while(cin>>x && x)
//把i的所有前驅比較一遍,找出最長時間,該時間正是i的最早開始時間
maxt = max(maxt,a[x]);
//自身最早完成時間 = 自身完成所需時間 + 所有前驅的最晚結束時間
a[i] += maxt;
//找出最晚結束的任務時間就是ans
maxans = max(maxans,a[i]);
}
cout<<maxans;
return 0;
}