题目描述
背景
在双人对决的竞技性比赛,如乒乓球、羽毛球、国际象棋中,最常见的赛制是淘汰赛和循环赛。前者的特点是
比赛场数少,每场都紧张刺激,但偶然性较高。后者的特点是较为公平,偶然性较低,但比赛过程往往十分冗长。
本题中介绍的瑞士轮赛制,因最早使用于1895年在瑞士举办的国际象棋比赛而得名。它可以看作是淘汰赛与循环赛的折中,既保证了比赛的稳定性,又能使赛程不至于过长。
描述:2*N 名编号为 1~2N 的选手共进行R 轮比赛。每轮比赛开始前,以及所有比赛结束后,都会按照总分从高到低对选手进行一次排名。选手的总分为第一轮开始前的初始分数加上已参加过的所有比赛的得分和。总分相同的,约定编号较小的选手排名靠前。
每轮比赛的对阵安排与该轮比赛开始前的排名有关:第1 名和第2 名、第 3 名和第 4名、……、第2K – 1 名和第 2K名、…… 、第2N – 1 名和第2N名,各进行一场比赛。每场比赛胜者得1 分,负者得 0 分。也就是说除了首轮以外,其它轮比赛的安排均不能事先确定,而是要取决于选手在之前比赛中的表现。
现给定每个选手的初始分数及其实力值,试计算在R 轮比赛过后,排名第 Q 的选手编号是多少。我们假设选手的实力值两两不同,且每场比赛中实力值较高的总能获胜。
输入
输入文件名为swiss.in 。
输入的第一行是三个正整数N、R 、Q,每两个数之间用一个空格隔开,表示有 2*N 名选手、R 轮比赛,以及我们关心的名次 Q。
第二行是2
N 个非负整数s1, s2, …, s2N,每两个数之间用一个空格隔开,其中 si 表示编号为i 的选手的初始分数。 第三行是2
N
个正整数w1 , w2 , …, w2N,每两个数之间用一个空格隔开,其中 wi 表示编号为i 的选手的实力值。
输出
输出文件名为swiss.out。
输出只有一行,包含一个整数,即R 轮比赛结束后,排名第 Q 的选手的编号。
样例输入
2 4 2
7 6 6 7
10 5 20 15
样例输出
1
提示
【样例解释】
【数据范围】
对于30% 的数据,1 ≤ N ≤ 100;
对于50% 的数据,1 ≤ N ≤ 10,000 ;
对于100%的数据,1 ≤ N ≤ 100,000,1 ≤ R ≤ 50,1 ≤ Q ≤ 2N,0 ≤ s1, s2, …, s2N≤10^8,1 ≤w1, w2 , …, w2N≤ 10^8
来源
全国青少年信息学奥林匹克联赛(NOIPOJ)在线评测系统
解题思路
每一个选手有3项信息:编号、得分和能力值。用players列表记住每个选手的信息,列表元素有3个数据项,第一项是编号,第二项是得分,第三项是能力值。一轮比赛后,得分这一项将发生变化。
对于每一轮比赛,首先确定谁与谁比,然后根据能力值计算各选手的得分。重复执行R轮,则可以得出各选手的最后得分。
如何确定谁与谁比?答案是,按照得分从高到低,得分相同的按编号从小到大对players列表进行排序。排序后,索引 i (i=0, 2, 4, …, 2*N-2)的选手与索引 i+1 的选手进行比赛。
最后,按照得分从高到低,得分相同的按编号从小到大对players列表进行排序。索引Q-1的列表元素的第一项就是最终结果。
参考答案
N, R, Q = [int(s) for s in input().split()]
scores = [int(s) for s in input().split()]
levels = [int(s) for s in input().split()]
players = [[i + 1, scores[i], levels[i]] for i in range(2*N)] #每个选手的编号、分数、能力
for r in range(R):
#按照得分从高到低,得分相同的按编号从小到大对players列表进行排序
players.sort() #按编号从小到大排序
players.sort(key = lambda p: p[1], reverse=True) #按得分从大到小排序
for i in range(0, 2*N, 2): #i的增量是2
if players[i][2] > players[i+1][2]: 第1 名和第2 名比、第 3 名和第 4名比、……
players[i][1] += 1
else:
players[i+1][1] += 1
players.sort() #按编号从小到大排序
players.sort(key = lambda p: p[1], reverse=True) #按得分从大到小排序
print(players[Q-1][0])
测试用例
题目描述给出的测试用例已覆盖一下情形:得分相同,编号小的在前。
N=1的边界情形。
样例输入
1 1 1
5 9
10 15
样例输出
2
得分始终不相同的情形。
样例输入
2 4 2
3 2 5 4
10 5 20 15
样例输出
4
根据题目描述给出的测试用例,可以扩展生成3个测试用例,分别输出排名为1的选手编号、输出排名为3的选手编号和输出排名为4的选手编号。由于扩展的成本低,不易犯错,因此值得使用。
小结
Python语言的官方手册推荐,按组合键【主关键字、次关键字】对列表排序,采用两步进行:(1)按次关键字对列表排序;(2)按主关键字对列表进行排序。排序的时间开销不是两次排序的时间开销累加。
利用题目描述给出的测试用例扩展生成新的测试用例,是好做法。