求拓撲序列

求一個有向圖的拓撲序列也是圖論的基本題型。但是一般不會顯式的看出題意是求拓撲序列或者求是否存在拓撲序列。拓撲序列一般用來判斷一個圖是否是一個有向無環圖,如果一個圖存在符合拓撲次序的序列則該圖是有向無環圖,反之則不是。

求拓撲序列步驟:

1,找到一個入度爲0的點作爲拓撲序列的第一個點

2,把該點和該點所有的邊從圖中刪去

3,再在新的圖中選擇一個入度爲0的點作爲拓撲系列的第二個點

...以此類推,如果在所有節點尚未刪去時找不到入度爲0的點則說明剩餘節點存在環路,不存在拓撲序列

九度1448:Legal or Not 題目地址:http://ac.jobdu.com/problem.php?pid=1448
題目描述:

ACM-DIY is a large QQ group where many excellent acmers get together. It is so harmonious that just like a big family. Every day,many "holy cows" like HH, hh, AC, ZT, lcc, BF, Qinz and so on chat on-line to exchange their ideas. When someone has questions, many warm-hearted cows like Lost will come to help. Then the one being helped will call Lost "master", and Lost will have a nice "prentice". By and by, there are many pairs of "master and prentice". But then problem occurs: there are too many masters and too many prentices, how can we know whether it is legal or not?We all know a master can have many prentices and a prentice may have a lot of masters too, it's legal. Nevertheless,some cows are not so honest, they hold illegal relationship. Take HH and 3xian for instant, HH is 3xian's master and, at the same time, 3xian is HH's master,which is quite illegal! To avoid this,please help us to judge whether their relationship is legal or not. Please note that the "master and prentice" relation is transitive. It means that if A is B's master ans B is C's master, then A is C's master.

輸入:

The input consists of several test cases. For each case, the first line contains two integers, N (members to be tested) and M (relationships to be tested)(2 <= N, M <= 100). Then M lines follow, each contains a pair of (x, y) which means x is y's master and y is x's prentice. The input is terminated by N = 0.TO MAKE IT SIMPLE, we give every one a number (0, 1, 2,..., N-1). We use their numbers instead of their names.

輸出:

For each test case, print in one line the judgement of the messy relationship.If it is legal, output "YES", otherwise "NO".

樣例輸入:
3 2
0 1
1 2
2 2
0 1
1 0
0 0
樣例輸出:
YES
NO

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
 
#define N 110
int inDegree[N];//保存每個節點的入度 
vector<int> adj_list[N];//鄰接表
queue<int> q;//保存入度爲0的節點 
 
int main(){
    int n,m;
    while(cin >> n){
        if(n == 0) break;
        cin >> m;
         
        for(int i = 0;i < n;i++){
            adj_list[i].clear();
            inDegree[i] = 0;
        }
         
        for(int i = 0;i < m;i++){
            int x,y;
            cin >> x >> y;
            inDegree[y]++;
            adj_list[x].push_back(y);
        }
         
        while(!q.empty()) q.pop();//清空隊列 
        for(int i = 0;i < n;i++){
            if(inDegree[i] == 0) q.push(i);
        }
         
        int cnt = 0;//用來記錄已確定的拓撲序列的節點個數
        while(!q.empty()){
            int node = q.front();
            q.pop();
            cnt++;
            for(int i = 0;i < adj_list[node].size();i++){
                inDegree[adj_list[node][i]]--;
                 
                if(inDegree[adj_list[node][i]] == 0){
                    q.push(adj_list[node][i]);
                }
            }
        }
         
        if(cnt == n) cout << "YES" << endl;
        else cout << "NO" << endl; 
    }
     
    return 0;
}
 
/**************************************************************
    Problem: 1448
    User: cherish
    Language: C++
    Result: Accepted
    Time:10 ms
    Memory:1524 kb
****************************************************************/

九度1449:確定比賽名次 題目地址:http://ac.jobdu.com/problem.php?pid=1449
題目描述:

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

輸入:

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

輸出:

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

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

樣例輸入:
4 3
1 2
2 3
4 3
樣例輸出:
1 2 4 3
求拓撲序列的題一般不會顯式的給出是要求出拓撲序列,要根據題意進行分析。這題就是要得到拓撲序列。

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
 
#define N 501
vector<int> adj_list[N];
int inDegree[N];
//建立一個小頂堆,在保存入度爲0的點的同時把較小的點放在前面 
priority_queue<int,vector<int>,greater<int> > q;
vector<int> top_seq;
 
int main(){
    int n,m;
    while(cin >> n){
        cin >> m;
         
        while(!q.empty()) q.pop();
        top_seq.clear();
        for(int i = 1;i <= n;i++){
            adj_list[i].clear();
            inDegree[i] = 0;
        }
         
        for(int i = 0;i < m;i++){
            int p1,p2;
            cin >> p1 >> p2;
            inDegree[p2]++;
            adj_list[p1].push_back(p2);
        }
         
        for(int i = 1;i <= n;i++)
            if(inDegree[i] == 0) q.push(i);
             
        while(!q.empty()){
            int p = q.top();
            q.pop();
            top_seq.push_back(p);
              
            for(int i = 0;i < adj_list[p].size();i++){
                inDegree[adj_list[p][i]]--;
                 
                if(inDegree[adj_list[p][i]] == 0) q.push(adj_list[p][i]);
            }
        }
         
        //輸出
        for(int i = 0;i < top_seq.size();i++){
            if(i == 0) cout << top_seq[i];
            else cout << " " << top_seq[i];
        } 
        cout << endl;
    }
     
    return 0;
}
 
/**************************************************************
    Problem: 1449
    User: cherish
    Language: C++
    Result: Accepted
    Time:20 ms
    Memory:1532 kb
****************************************************************/

九度1450:產生冠軍 題目地址:http://ac.jobdu.com/problem.php?pid=1450
題目描述:

有一羣人,打乒乓球比賽,兩兩捉對撕殺,每兩個人之間最多打一場比賽。
球賽的規則如下:
如果A打敗了B,B又打敗了C,而A與C之間沒有進行過比賽,那麼就認定,A一定能打敗C。
如果A打敗了B,B又打敗了C,而且,C又打敗了A,那麼A、B、C三者都不可能成爲冠軍。
根據這個規則,無需循環較量,或許就能確定冠軍。你的任務就是面對一羣比賽選手,在經過了若干場撕殺之後,確定是否已經實際上產生了冠軍。

輸入:

輸入含有一些選手羣,每羣選手都以一個整數n(n<1000)開頭,後跟n對選手的比賽結果,比賽結果以一對選手名字(中間隔一空格)表示,前者戰勝後者。如果n爲0,則表示輸入結束。

輸出:

對於每個選手羣,若你判斷出產生了冠軍,則在一行中輸出“Yes”,否則在一行中輸出“No”。

樣例輸入:
3
Alice Bob
Smith John
Alice Smith
5
a c
c d
d e
b e
a d
0

#include<iostream>
#include<queue>
#include<map>
#include<string>
using namespace std;
 
#define N 1001
map<string,int> players;
int inDegree[N];
 
int main(){
    int n;
    while(cin >> n){
        if(n == 0) break;
         
        for(int i = 1;i <= N;i++)
            inDegree[i] = 0;
        players.clear();
         
        int vertex = 1;//記錄節點數 
        for(int i = 1;i <= n;i++){
            string name1,name2;
            cin >> name1 >> name2;
            if(players.find(name1) == players.end()) players[name1] = vertex++;
            if(players.find(name2) == players.end()) players[name2] = vertex++;
            inDegree[players[name2]]++;
        }
         
        int cnt = 0;
        for(int i = 1;i <= vertex - 1;i++){
            if(inDegree[i] == 0) cnt++;
        }
        if(cnt == 1) cout << "Yes" << endl;
        else cout << "No" << endl;
    }
     
    return 0;
}
 
/**************************************************************
    Problem: 1450
    User: cherish
    Language: C++
    Result: Accepted
    Time:40 ms
    Memory:1528 kb
****************************************************************/

總結:

求拓撲序列需要一個數組inDegree[]來保存各個點的入度;需要鄰接矩陣或者鄰接表保存邊;需要保存當前入度爲0的點(如隊列)。做算法題感覺數據結構很重要,選對數據結構是算法能否實現或者能否很好的實現的關鍵。

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