拓扑排序应用问题

题意:
猫猫比赛。一共有 N 只猫猫,编号依次为1,2,3,…,N进行比赛。比赛结束后,会为所有的猫猫从前到后依次排名。每场比赛的结果都记录了下来,现在编程确定字典序最小的名次序列。
输入:
输入有若干组,每组中的第一行为二个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
解题思路:
猫猫们之间的胜负关系可以构成一张有向无环图,P1赢了P2在图中就等价于结点P1到结点P2有一条有向边。P1 赢了 P2 意味着在最终的名次序列中 P1 要在 P2 的前面。我们将问题转化为求字典序最小的拓扑序。任一时刻队列中所有结点意味着已经可以确定名次关系并且互相之间没有依赖的猫猫。要使得字典序最小,我们取队首元素的时候可以取队列中编号最小的点出队,这样我们可以使用优先级队列来替换普通的队列来实现这一要求。
注意事项:
尽管用优先级队列实现,但注意我们要使得字典序最小,所以我们加入队列中的元素要取反,就成了小根堆了,弹出数据时不要忘记再次要取反才能得到原始数据。
总结:
一道考察拓扑排序的题目,题目并不难,基本是模板题,掌握求拓扑序列的算法即可,并注意字典序最小用优先级队列实现,并通过取反实现小根堆。拓扑排序的题目一般难度都不大,但需要学会转换,把胜负关系,同学关系,课程关系等生活中的关系转化到图中的边与点来。
参考代码:

#include 
#include 
#include 
using namespace std;
priority_queue<int> pq;
vector<int> v;
int n,m;
int in_degree[505];
struct edge{
    int to, next ,w;
    edge(int a=0,int b=-1,int c=0):to(a),next(b),w(c){}
} e[125000];
int head[505],tot=0;
void add(int x,int y,int w){
    e[tot].to = y;
    e[tot].next = head[x];
    e[tot].w = w;
    head[x] = tot;
    tot++;
}
int main(int argc, const char * argv[]) {
    while (cin>>n>>m) {
        memset(in_degree, 0, sizeof(in_degree));
        memset(head, -1, sizeof(head));
        v.clear();
        while (pq.size()) {
            pq.pop();
        }
        int a,b;
        while (m--) {
            cin>>a>>b;
            add(a, b, 1);in_degree[b]++;
        }
        for (int i=1; i<=n; i++) {
            if(in_degree[i]==0)pq.push(-i);
        }
        while (pq.size()) {
            int u=-pq.top();pq.pop();
            v.push_back(u);
            for(int i=head[u];i!=-1;i=e[i].next) {
            int v = e[i].to;
                if(--in_degree[v]==0)pq.push(-v);
            }
        }
        if(v.size()==n){
            for (int i=0; i<n-1; i++) {
                cout<<v[i]<<" ";
            }
            cout<<v[n-1]<<endl;
        }
    }
    return 0;
}

你可能感兴趣的:(拓扑排序应用问题)