题目背景
在双人对决的竞技性比赛,如乒乓球、羽毛球、国际象棋中,最常见的赛制是淘汰赛和循环赛。前者的特点是比赛场数少,每场都紧张刺激,但偶然性较高。后者的特点是较为公平,偶然性较低,但比赛过程往往十分冗长。
本题中介绍的瑞士轮赛制,因最早使用于18951895年在瑞士举办的国际象棋比赛而得名。它可以看作是淘汰赛与循环赛的折中,既保证了比赛的稳定性,又能使赛程不至于过长。
题目描述
2×N 名编号为 1∼2N 的选手共进行R 轮比赛。每轮比赛开始前,以及所有比赛结束后,都会按照总分从高到低对选手进行一次排名。选手的总分为第一轮开始前的初始分数加上已参加过的所有比赛的得分和。总分相同的,约定编号较小的选手排名靠前。
每轮比赛的对阵安排与该轮比赛开始前的排名有关:第11 名和第22 名、第 33 名和第 44名、……、第2K - 12K−1名和第2K2K名、…… 、第2N - 12N−1名和第2N2N名,各进行一场比赛。每场比赛胜者得11分,负者得 00分。也就是说除了首轮以外,其它轮比赛的安排均不能事先确定,而是要取决于选手在之前比赛中的表现。
现给定每个选手的初始分数及其实力值,试计算在R 轮比赛过后,排名第QQ 的选手编号是多少。我们假设选手的实力值两两不同,且每场比赛中实力值较高的总能获胜。
输入格式
第一行是三个正整数N,R ,QN,R,Q,每两个数之间用一个空格隔开,表示有2×N名选手、RR 轮比赛,以及我们关心的名次 QQ。
▶用数组依次表示选手编号,当前得分,实力值
▶编写cmp函数,先对选手按照初始分数从大到小排列,分数相同者编号从小到大排列
▶每轮赛后用归并排序(merge函数)对所有选手进行排序。分成胜者组和败者组,此时两个数组一定都按照分数由大到小排列,因此比较两数组的最大值,分数高者加进总序列,分数低者继续和另一个数组的次高值比较,分数高者再加进总序列。直到两个数组的元素都加进总序列。
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 200100;
int n, r, q;
int s[N], w[N];//s表示选手的初始分数,w表示选手的实力值
int a[N], win[N], lose[N];//a表示每位选手的编号,win[]存储每局胜利者的下标,lose[]存储每局失败者的下标
bool cmp(int x, int y)//判断x,y的初始分数,若初始分数相同,编号小的优先
{
if(s[x]==s[y]) return x<y;
return s[x]>s[y];
}
void merge()//归并函数,使有序的win,lose有序归并为到a中
{
int i, j;
i = j = 1, a[0] = 0;//a[0]代替k,记录a[]中有多少个数
while(i <= win[0] && j <= lose[0])
{
if(cmp(win[i], lose[j])) a[++a[0]] = win[i++];
else a[++a[0]] = lose[j++];
}
while(i <= win[0]) a[++a[0]] = win[i++];//若败者组已全部加进序列,胜者组有剩则依次加进总序列
while(j <= lose[0]) a[++a[0]] = lose[j++];//若胜者组已全部加进序列,败者组依次进总序列
}
int main()
{
cin >> n >> r >> q; n *= 2;
for(int i = 1; i <= n; i++) a[i] = i;
for(int i = 1; i <= n; i++) cin >> s[i];
for(int i = 1; i <= n; i++) cin >> w[i];
sort(a + 1, a + n + 1, cmp);//将选手按照初始分数降序排列
while(r--)//进行r次比赛
{
win[0] = lose[0] = 0;
for(int i = 1; i <= n ; i += 2)//注意相邻两个选手比赛 i += 2,从奇数开始;
{
if(w[a[i]] > w[a[i + 1]])//比较相邻两位选手的实力值,实力值大的选手获胜
{
s[a[i]]++;//本轮分数加1
win[++win[0]] = a[i];//将胜利者的下标存入win中
lose[++lose[0]] = a[i + 1];
}
else
{
s[a[i + 1]]++;
win[++win[0]] = a[i + 1];
lose[++lose[0]] = a[i];
}
}
merge();
}
cout << a[q];
return 0;
}