[week8]猫猫向前冲——拓扑排序

题意

【🐱又来了555】
众所周知, TT 是一位重度爱猫人士,他有一只神奇的魔法猫。
有一天,TT 在 B 站上观看猫猫的比赛。一共有 N 只猫猫,编号依次为1,2,3,…,N进行比赛。比赛结束后,Up 主会为所有的猫猫从前到后依次排名并发放爱吃的小鱼干。不幸的是,此时 TT 的电子设备遭到了宇宙射线的降智打击,一下子都连不上网了,自然也看不到最后的颁奖典礼。
不幸中的万幸,TT 的魔法猫将每场比赛的结果都记录了下来,现在他想编程序确定字典序最小的名次序列,请你帮帮他。

Input

输入有若干组,每组中的第一行为二个数N(1<=N<=500),M;其中N表示猫猫的个数,M表示接着有M行的输入数据。接下来的M行数据中,每行也有两个整数P1,P2表示即编号为 P1 的猫猫赢了编号为 P2 的猫猫。

Output

给出一个符合要求的排名。输出时猫猫的编号之间有空格,最后一名后面没有空格!

其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。

输入样例

4 3
1 2
2 3
4 3

输出样例

1 2 4 3

提示


分析

这道题就是经典的拓扑排序问题。

  • 拓扑排序
  1. 什么是拓扑排序

拓扑排序指的就是在一个有向无环图中,规定所有点都必须在指向它的所有点都遍历过才能遍历。

也就是说,任何点都不能比指向它的点先出现。

有点抽象,那么来个🌰:

小刚在大学学习课程。学校规定了学习完a课程之后,才能学习b和c课程,而d课程必须在学习完a和c之后。显然,小刚要学习c课程,就必须要先学习a课程,再学习c课程,然后才能学到c。

  1. 如何求解拓扑排序

求解拓扑排序用到的是kahn算法。只要理解了拓扑排序,求解算法其实很简单。

选择一个入度为0的点开始遍历。对当前取出点邻接的所有点进行遍历,所有邻接点的入度都将-1。再判断是否入度变为0,若入度变为0,则该点从图中被取出作为备选。

如果最后取出的点数等于点的总数,那么该图就存在拓扑排序,否则就不存在,该图中一定存在环路。

💡kahn算法代码模板

在这里插入图片描述


  • 代码实现

本题就是一道经典的拓扑排序题,因此理解算法后就能参考模版得到正确的代码。

在实现拓扑排序时,要用一个数组记录每一个点的入度,配合算法实现。

注意本题的输出要求❗️


总结

  1. 最小优先级队列的stl还需要两个头文件😰

代码

//
//  main.cpp
//  lab2
//
//

#include <iostream>
#include <queue>
#include <vector>
#include <deque>
#include <functional>
using namespace std;

struct edge
{
    int start,end,value,next;  //存储一个区间[start,end]中至少要选出的点数num
}Edges[10000];

int head[600],in_degree[600];
int tot;
priority_queue<int, deque<int>, greater<int>> q;
vector<int> order;

void ini(int n)
{
    tot = 0;
    for( int i = 0 ; i < n ; i++ )
        head[i] = -1;
    
    
}

void add(int s,int e,int w)
{
    Edges[tot].start = s;
    Edges[tot].end = e;
    Edges[tot].value = w;
    Edges[tot].next = head[s];
    head[s] = tot;
    tot++;
}

void topo(int n)
{
    for( int i = 1 ; i <= n ; i++ )     //找到所有没有入度的点,压入队列中
    {
        if( in_degree[i] == 0 )
            q.push(i);
    }
    
    while( !q.empty() )
    {
        int now = q.top();      //取出当前最小的点
        q.pop();
        
        order.push_back(now);   //加入答案序列中
        
        for( int i = head[now] ; i != -1 ; i = Edges[i].next )      //遍历该点的邻接点
        {
            int to = Edges[i].end;
            
            in_degree[to]--;        //所有与该点邻接的点的入度都将-1,因为该点已经到达
            
            if( in_degree[to] == 0 )        //若入度为0,则压入队列
                q.push(to);
        }
        
    }
    
    //由于队列是最小优先级队列,因此若每次都取队首点加入序列,最后得到序列一定是最小字典序
    if( order.size() == n )     //若所有点都得到,拓扑序列才成功
    {
        for( int i = 0 ; i < n - 1 ; i++ )
            cout<<order[i]<<" ";
        cout<<order[n - 1]<<endl;
    }
    else
        cout<<-1<<endl;
    
}

int main()
{
    ios::sync_with_stdio(false);
    
    int n = 0,m = 0,a = 0,b = 0;
    
    
    while( cin>>n>>m )
    {
        ini(n+1);
        
        for( int i = 1; i <= n ; i++ )
            in_degree[i] = 0;
        
        order.clear();
        
        while( m-- )
        {
            cin>>a>>b;
            
            add(a, b, 1);       //记录所有赢者边
            in_degree[b]++;     //输者的入度+1
        }
        
/*        for( int i = 1 ; i <= n ; i++ )
            cout<<in_degree[i]<<" ";
        cout<<" ----- "<<endl;*/
        
        topo(n);
        
    }
    
    
    
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天翊藉君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值