求拓扑序列

求一个有向图的拓扑序列也是图论的基本题型。但是一般不会显式的看出题意是求拓扑序列或者求是否存在拓扑序列。拓扑序列一般用来判断一个图是否是一个有向无环图,如果一个图存在符合拓扑次序的序列则该图是有向无环图,反之则不是。

求拓扑序列步骤:

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的点(如队列)。做算法题感觉数据结构很重要,选对数据结构是算法能否实现或者能否很好的实现的关键。

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