数据结构与算法-23确定比赛名次

Description
有N个比赛队(1<=N<=500),编号依次为1,2,3,……,N进行比赛,比赛结束后,裁判委员会要将所有参赛队伍从前往后依次排名,但现在裁判委员会不能直接获得每个队的比赛成绩,只知道每场比赛的结果,即P1赢P2,用P1,P2表示,排名时P1在P2之前。现在请你编程序确定排名。

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

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

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

Sample Input

4 3
1 2
2 3
4 3

Sample Output

1 2 4 3

参考程序

def Out(ls):
    for i in range(len(ls) - 1):
        print(ls[i], end=" ")
    print(ls[-1])


while True:
    try:
        N, M = map(int, input().split())
        pre_dic = {}
        next_dic = {}
        for i in range(1, N + 1):
            pre_dic[i] = []
            next_dic[i] = []
        for i in range(M):
            a, b = map(int, input().split())
            pre_dic[b].append(a)
            next_dic[a].append(b)
        res = []

        while True:
            FirstNodeList = []
            for i in range(1, N + 1):  # 查找没有前驱结点的结点集合
                if i in pre_dic.keys() and len(pre_dic[i]) == 0:
                    FirstNodeList.append(i)
            node = min(FirstNodeList)  # 找出序号最小的结点node
            for n in next_dic[node]:  # n是node的各后继结点
                pre_dic[n].remove(node)
                M -= 1
            pre_dic.pop(node)
            res = res + [node]
            if M == 0:  # 所有边都已经去掉
                break
        tail = list(pre_dic.keys())  # 所有的边去掉后,图中剩余的最后几个没有前驱的结点
        tail.sort()
        res += tail
        Out(res)
    except:
        break

思路分析:
本题的题意是根据每行输入的部分次序关系,从而还原整个比赛程序的次序关系。刻画问题时,可以将队编号用结点表示,对之间的次序关系用有向边表示,那么每行输入的P1,P2表示P1队赢了P2队,就可以表示为P1→P2。这样M行的输入数据实际上就是给出的M条有向边。这样问题的全局表示就刻画出来了,实际上是一张有向无环图。接下来确定全局次序关系的思路是,每一步扫描所有没有前驱结点的结点,这些结点是“候选第1名”,从中选取序号最小的,计入结果列表,它就是全局第1名,然后将此结点从图中删除,并把该结点发出的所有边删除。重复此过程直至所有边删除完毕,最后剩余的结点再按照标号从小到大排序,并入到结果列表中

实现方法:

  • 本题使用Python语言,图的模拟采用字典这一数据结构。
  • pre_dic字典中键表示结点编号,值表示该结点的前驱集合(列表)。列表为空则表示没有前驱结点;
  • nexte_dic字典中键表示结点编号,值表示该节点的后继集合。
  • 删除结点时从pre_dic字典删除键(用pop方法)即可。
  • 删除边的方法比较复杂:例如删除结点node发出的所有边,首先找出node的所有后继结点,然后到pre_dic字典中访问每这些结点的前驱列表,逐一删除node号结点(用remove方法)。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值