日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

拓扑排序基础讲解

發(fā)布時間:2023/12/19 编程问答 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 拓扑排序基础讲解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

拓撲排序(TopSort)

2021年8月5日

文章目錄

  • 拓撲排序(TopSort)
      • 1、算法原理
        • 1.1 一個問題
        • 1.2 如何通過計算機輸出排序結(jié)果?
        • 1.3 圖示
        • 1.4 不穩(wěn)定性
        • 1.5 無法排序?
        • 1.6 時間復(fù)雜度
      • 2、算法實現(xiàn)
        • 2.1 題目
        • 2.2 解法分析
        • 2.3 完整代碼
      • 幾道例題
          • A:[POJ - 2367 ](https://vjudge.net/problem/POJ-2367/origin) Genealogical tree
          • B:[HDU - 1285](https://vjudge.net/problem/HDU-1285/origin)[ 確定比賽名次](https://vjudge.net/problem/HDU-1285)
          • C:[HDU - 2094](https://vjudge.net/problem/HDU-2094/origin)[產(chǎn)生冠軍](https://vjudge.net/problem/HDU-2094)

1、算法原理

1.1 一個問題

在日常生活中,我們的某些行為需要有先后順序。比如,你需要先穿襪子再穿鞋,先拿起手表再戴手表,順序無法改變。

同理在一個工程中,每一步的工作都需要先后順序,某些工作的完成是另一些工作的前置條件。我們稱這每一步的工作為事件。

那么,若已知每個事件的前置事件,怎么確定其完成的先后順序?

我們可以構(gòu)建一個網(wǎng)絡(luò),將每個事件的前置事件放置在這個事件之前,并用有向線段連接,由此可以直觀地看出我們需要完成事件的先后順序。

此時構(gòu)建的網(wǎng)絡(luò),通常被稱為頂點活動網(wǎng)(Activity On Vertex network),簡稱AOV網(wǎng)。

1.2 如何通過計算機輸出排序結(jié)果?

拓撲排序就是針對這一類問題進行的排序算法。

建立上述AOV網(wǎng)之后,我們可對每個事件所構(gòu)成的結(jié)點進行入度的記錄。

首先,將所有入度為0的結(jié)點加入隊列(這些事件不需要前置事件),對其后續(xù)結(jié)點進行遍歷。遍歷到一個結(jié)點后,此結(jié)點的入度就減1,當(dāng)入度減為0,加入隊列(前置事件均已完成),對其后續(xù)結(jié)點進行遍歷。

當(dāng)所有結(jié)點遍歷過后,就可以知道其先后順序了。

1.3 圖示

首先,將入度為0的1,2,4結(jié)點加入隊列,首先遍歷1后的結(jié)點:

此時,結(jié)點3的入度減去1,變?yōu)?,不等于0,先不進行操作,隨后,對2后的結(jié)點進行遍歷:

此時,結(jié)點3的入度減為0,加入隊列。

接著,如下操作:

所有結(jié)點遍歷完畢,其先后順序即為 1 2 4 3 5 6。

1.4 不穩(wěn)定性

拓撲排序無其他排序條件時是不穩(wěn)定的,與你加入隊列的順序與方式有關(guān),比如上面圖示樣例中也可寫成1 4 2 3 5 6,4 1 2 3 5 6等等。

因為其在實際問題中,同層的先后順序是無關(guān)事件,因此不影響其正確性,若對排序有另外的要求,則按照要求的方式加入隊列即可。

1.5 無法排序?

當(dāng)圖是有環(huán)圖,意味著一定有結(jié)點無法將其入度減為0,因此無法進行其后續(xù)結(jié)點的排序,如圖:

此時不存在入度為0的點,因此無法得知其先后順序。

因此,我們需要在進行排序時,判斷所有遍歷過的點數(shù)目是否等于所有點的數(shù)目,若不等于,則表示其存在環(huán),排序失敗,若等于,則排序成功。

1.6 時間復(fù)雜度

因每個結(jié)點只需加入隊列一次,而每個結(jié)點加入隊列前需要進行入度數(shù)量次的操作,因此時間復(fù)雜度為O(n+m)。

2、算法實現(xiàn)

2.1 題目

給你n個有向邊和m個點,若能進行拓撲排序,輸出排序后的序列,若不能,輸出-1.

輸出為1行,答案可能不唯一,輸出正解中任意一種。

2.2 解法分析

首先,記錄所有點的入度,此時設(shè)置數(shù)組(或其他記錄方式)inp[],在每次輸入時,對后一個事件的入度進行加1,并記錄前一個事件的后續(xù)事件:

for(int i = 0; i < n; i++){cin>>a>>b;inp[b]++;v[a].push_back(b); }

隨后,將每個入度為0的結(jié)點加入隊列:

queue<int>q; for (int i = 1; i <= m; i++) {//注意點的標(biāo)號從幾開始if (!inp[i])q.emplace(i);//為0則加入隊列 }

接著,對圖按照BFS進行遍歷:

while (!q.empty()) {int fa = q.front();q.pop();pd++;//點數(shù)量的記錄ans[num++] = fa;//記錄順序for (auto now : v[fa]) {inp[now]--;//入度減一if (!inp[now]) {//為0則加入隊列q.emplace(now);}} }

最后對是否排序成功進行判斷:

if(pd==m)return 1; else return 0;

2.3 完整代碼

#include<iostream> #include<algorithm> #include<stdio.h> #include<vector> #include<queue>using namespace std;int inp[1005]; int n, m, pd; vector<int>v[1005]; int ans[1005];bool bfs() {queue<int>q;for (int i = 1; i <= m; i++) {if (!inp[i])q.emplace(i);//為0則加入隊列}while (!q.empty()) {int fa = q.front();q.pop();ans[pd++] = fa;for (auto now : v[fa]) {inp[now]--;//入度減一if (!inp[now]) {//為0則加入隊列q.emplace(now);}}}if (pd == m)return true;else return false; }int main() {int a, b;cin >> n >> m;for (int i = 0; i < n; i++) {cin >> a >> b;inp[b]++;v[a].push_back(b);}if (bfs()) {cout << ans[0];for (int i = 1; i < m; i++){cout << ' ' << ans[i];}}else {cout << -1;}cout << endl;return 0; }

樣例輸入1:

5 6 1 3 2 3 3 5 4 5 5 6

預(yù)期結(jié)果1:

1 2 4 3 5 6

測試結(jié)果1:

樣例輸入2:

6 6 1 2 2 3 3 4 4 5 5 2 4 6

預(yù)期結(jié)果2:

-1

測試結(jié)果2:

以上樣例均通過。

幾道例題

A:POJ - 2367 Genealogical tree

Description

The system of Martians’ blood relations is confusing enough. Actually, Martians bud when they want and where they want. They gather together in different groups, so that a Martian can have one parent as well as ten. Nobody will be surprised by a hundred of children. Martians have got used to this and their style of life seems to them natural.
And in the Planetary Council the confusing genealogical system leads to some embarrassment. There meet the worthiest of Martians, and therefore in order to offend nobody in all of the discussions it is used first to give the floor to the old Martians, than to the younger ones and only than to the most young childless assessors. However, the maintenance of this order really is not a trivial task. Not always Martian knows all of his parents (and there’s nothing to tell about his grandparents!). But if by a mistake first speak a grandson and only than his young appearing great-grandfather, this is a real scandal.
Your task is to write a program, which would define once and for all, an order that would guarantee that every member of the Council takes the floor earlier than each of his descendants.

Input

The first line of the standard input contains an only number N, 1 <= N <= 100 — a number of members of the Martian Planetary Council. According to the centuries-old tradition members of the Council are enumerated with the natural numbers from 1 up to N. Further, there are exactly N lines, moreover, the I-th line contains a list of I-th member’s children. The list of children is a sequence of serial numbers of children in a arbitrary order separated by spaces. The list of children may be empty. The list (even if it is empty) ends with 0.

Output

The standard output should contain in its only line a sequence of speakers’ numbers, separated by spaces. If several sequences satisfy the conditions of the problem, you are to write to the standard output any of them. At least one such sequence always exists.

Sample Input

5
0
4 5 1 0
1 0
5 3 0
3 0

Sample Output

2 4 5 3 1

分析

此題輸入第i個結(jié)點的后續(xù)結(jié)點,直接建圖拓撲排序即可。

代碼

#include<iostream> #include<algorithm> #include<stdio.h> #include<vector> #include<queue>using namespace std;int inp[1005], n, pd, num; vector<int>v[1005]; int ans[1005];bool bfs() {queue<int>q;for (int i = 1; i <= n; i++) {if (!inp[i])q.emplace(i);//為0則加入隊列}while (!q.empty()) {int fa = q.front();q.pop();ans[pd++] = fa;for(int i = 0; i < v[fa].size(); i++){int now = v[fa][i];inp[now]--;//入度減一if (!inp[now]) {//為0則加入隊列q.emplace(now);}}}if (pd == n)return true;else return false; }int main() {int a;cin >> n;for (int i = 0; i < n; i++) {while (cin >> a, a != 0) {v[i + 1].push_back(a);inp[a]++;}}if (bfs()) {cout << ans[0];for (int i = 1; i < num; i++) {cout << ' ' << ans[i];}}else {cout << -1;}cout << endl;return 0; }
B:HDU - 1285 確定比賽名次

Problem Description

有N個比賽隊(1<=N<=500),編號依次為1,2,3,。。。。,N進行比賽,比賽結(jié)束后,裁判委員會要將所有參賽隊伍從前往后依次排名,但現(xiàn)在裁判委員會不能直接獲得每個隊的比賽成績,只知道每場比賽的結(jié)果,即P1贏P2,用P1,P2表示,排名時P1在P2之前。現(xiàn)在請你編程序確定排名。

Input

輸入有若干組,每組中的第一行為二個數(shù)N(1<=N<=500),M;其中N表示隊伍的個數(shù),M表示接著有M行的輸入數(shù)據(jù)。接下來的M行數(shù)據(jù)中,每行也有兩個整數(shù)P1,P2表示即P1隊贏了P2隊。

Output

給出一個符合要求的排名。輸出時隊伍號之間有空格,最后一名后面沒有空格。

其他說明:符合條件的排名可能不是唯一的,此時要求輸出時編號小的隊伍在前;輸入數(shù)據(jù)保證是正確的,即輸入數(shù)據(jù)確保一定能有一個符合要求的排名。

Sample Input

4 3
1 2
2 3
4 3

Sample Output

1 2 4 3

分析:

此題要求隊伍編號小的隊伍在前,因此需要使用優(yōu)先隊列,然后建圖拓撲排序即可。

代碼:

#include<iostream> #include<algorithm> #include<stdio.h> #include<vector> #include<queue> #include<functional>using namespace std;int inp[505], n, m; vector<int>v[505]; int ans[505];void bfs() {//queue<int>q;int pd = 0;priority_queue<int, vector<int>, greater<int>>q;//因題目要求,此處采用優(yōu)先隊列for (int i = 1; i <= n; i++) {if (!inp[i])q.emplace(i);//為0則加入隊列}while (!q.empty()) {int fa = q.top();q.pop();ans[pd++] = fa;for(int i = 0; i < v[fa].size(); i++){int now = v[fa][i];inp[now]--;//入度減一if (!inp[now]) {//為0則加入隊列q.emplace(now);}}} }int main() {int a, b;while (cin>>n>>m) {memset(inp, 0, sizeof inp);memset(v, 0, sizeof v);for (int i = 0; i < m; i++) {cin >> a >> b;inp[b]++;v[a].push_back(b);}bfs();cout << ans[0];for (int i = 1; i < n; i++) {cout << ' ' << ans[i];}cout << '\n';}return 0; }
C:HDU - 2094產(chǎn)生冠軍

Problem Description

有一群人,打乒乓球比賽,兩兩捉對撕殺,每兩個人之間最多打一場比賽。
球賽的規(guī)則如下:
如果A打敗了B,B又打敗了C,而A與C之間沒有進行過比賽,那么就認(rèn)定,A一定能打敗C。
如果A打敗了B,B又打敗了C,而且,C又打敗了A,那么A、B、C三者都不可能成為冠軍。
根據(jù)這個規(guī)則,無需循環(huán)較量,或許就能確定冠軍。你的任務(wù)就是面對一群比賽選手,在經(jīng)過了若干場撕殺之后,確定是否已經(jīng)實際上產(chǎn)生了冠軍。

Input

輸入含有一些選手群,每群選手都以一個整數(shù)n(n<1000)開頭,后跟n對選手的比賽結(jié)果,比賽結(jié)果以一對選手名字(中間隔一空格)表示,前者戰(zhàn)勝后者。如果n為0,則表示輸入結(jié)束。

Output

對于每個選手群,若你判斷出產(chǎn)生了冠軍,則在一行中輸出“Yes”,否則在一行中輸出“No”。

Sample Input

3
Alice Bob
Smith John
Alice Smith
5
a c
c d
d e
b e
a d
0

Sample Output

Yes
No

分析:

此題是考察拓撲排序的性質(zhì),即若冠軍只有一人的話,則初始化圖的入度為0的僅有一個結(jié)點,因此直接枚舉入度為0結(jié)點數(shù)量即可。

代碼:

#include<iostream> #include<algorithm> #include<stdio.h> #include<string> #include<map>using namespace std;map<string, int>mp;bool solve(int n) {int poc = 0;for (auto x : mp) {if (!x.second) {poc++;}}if (poc == 1)return 1;elsereturn 0; } int main() {int n;while (cin >> n, n != 0) {mp.clear();string k, l;for (int i = 0; i < n; i++) {cin >> k >> l;mp[k] = mp[k];mp[l]++;}if (solve(n))cout << "Yes\n";elsecout << "No\n";}return 0; }

總結(jié)

以上是生活随笔為你收集整理的拓扑排序基础讲解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。