拓撲排序
轉載自: https://blog.csdn.net/y_universe/article/details/79342940
定義
對一個有向無環圖(Directed Acyclic Graph簡稱DAG) G進行拓撲排序,是將G中所有頂點排成一個線性序列,使得圖中任意一對頂點u和v,若邊(u,v)∈E(G),則u在線性序列中出現在v之前。通常,這樣的線性序列稱爲滿足拓撲次序(Topological Order)的序列,簡稱拓撲序列。簡單的說,由某個集合上的一個偏序得到該集合上的一個全序,這個操作稱之爲拓撲排序。
在AOV網中,若不存在迴路,則所有活動可排列成一個線性序列,使得每個活動的所有前驅活動都排在該活動的前面,我們把此序列叫做拓撲序列(Topological order),由AOV網構造拓撲序列的過程叫做拓撲排序(Topological sort)。AOV網的拓撲序列不是唯一的,滿足上述定義的任一線性序列都稱作它的拓撲序列。
換句話說: 在實踐的解題中,多用於求解具有嚴格順序要求的活動及這個活動所耗費的時間。如要完成B活動,首先要完成A活動纔行,也就是說只有A活動完成了才能做B活動。具體見後面例題解析。
拓撲排序的實現步驟
1. 從圖中選擇一個沒有前驅(即入度爲0)的頂點並輸出。
2. 刪除該頂點和所有以它爲起點的有向邊。
重複 1 和 2 直到當前的圖爲空或當前圖中不存在無前驅的頂點爲止。若當前圖中不存在無前驅的頂點說明有向圖中必存在環。(ps:存在環的情況也就是題目中無解的情況)
例題1:最短工期 (25分)
輸入樣例 1:
9 12
0 1 6
0 2 4
0 3 5
1 4 1
2 4 1
3 5 2
5 4 0
4 6 9
4 7 7
5 7 4
6 8 2
7 8 4
輸出樣例 1:
18
輸入樣例 2:
4 5
0 1 1
0 2 2
2 1 3
1 3 4
3 2 5
輸出樣例 2:
Impossible
解析:
就如題目所說的,一個項目的任務也是有先後依賴關係的,也就是說是有嚴格的順序執行的。這裏就可以用到拓撲排序,只是這裏不是求任務完成順序,而是最早完工時間。
具體解析見AC代碼:
#include<bits/stdc++.h>
using namespace std;
int dis[105][105],ru[105],f[105];
int n,ans,cnt=0;
void topological_sort(){
while(1){
int flag=0;
for(int i=0;i<n;i++){
if(!ru[i]){ //入度爲0的頂點(即沒有前驅任務的任務,那麼這個任務就可以做了)
ru[i]--;//變爲-1,也就是這個任務做完了,後面不用考慮了
cnt++;
flag=1;
for(int j=0; j<n; j++){
if(dis[i][j]!=-1){//存在以i爲前驅任務的任務
ru[j]--; // 入度減一(把i任務去掉了,則j的前驅任務少了一個)
f[j]=max(f[j],f[i]+dis[i][j]); //選擇最長的工作時間
ans=max(ans,f[j]);//統計最早完工時間
}
}
}
}
if(!flag) break; //不存在入度爲0的點,跳出循環
}
if(cnt==n) cout<<ans<<endl; //全部頂點和邊成功去掉
else cout<<"Impossible"<<endl;//存在環
}
int main(){
int m, p, x, y;
cin>>n>>m;
for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
dis[i][j]=-1;//初始邊的權都爲-1,因爲工作時長可能是0,所以初始dis都賦值爲-1
while(m--){
cin>>x>>y>>p;
dis[x][y]=p;
ru[y]++;//ru[y]頂點y的入度,(即題目中的y任務的前驅任務們)
}
topological_sort(); //拓撲排序
return 0;
}
優化:
用vector,queue優化的算法,多用於只求活動執行的順序,不需要求權值
void topological_sort(){
queue<int>q;
vector<int>edge[n];
for(int i=0;i<n;i++) //n:結點的總數
if(in[i]==0) q.push(i); //將入度爲0的點入隊列
vector<int>ans; //ans 爲拓撲序列
while(!q.empty())
{
int p=q.front(); q.pop(); // 選一個入度爲0的點,出隊列
ans.push_back(p);
for(int i=0;i<edge[p].size();i++)
{
int y=edge[p][i];
in[y]--;
if(in[y]==0) q.push(y);
}
}
if(ans.size()==n){
for(int i=0;i<ans.size();i++)
cout<<ans[i]<<" ";
}else cout<<"No Answer!\n"; // ans中的長度與n不相等,存在環
}
推薦題目:https://acm.zcmu.edu.cn/JudgeOnline/problem.php?id=2153
推薦博客:https://blog.csdn.net/qq_41713256/article/details/80805338
歡迎大家批評改正!!!