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
43
12
23
43
Sample Output
12 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方法)。
版权声明:本文不是「本站」原创文章,版权归原作者所有 | 原文地址: