PTA:最短工期 (25分)(拓撲排序筆記--入門到解題)

拓撲排序

轉載自: 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
歡迎大家批評改正!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章