洛谷Oj-P1113 雜務-拓撲排序
阿新 • • 發佈:2019-01-26
問題描述:
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