【排序】瑞士轮
题意 & 分析
在双人对决的竞技性比赛,如乒乓球、羽毛球、国际象棋中,最常见的赛制是淘汰赛和循环赛。前者的特点是比赛场数少,每场都紧张刺激,但偶然性较高。后者的特点是较为公平,偶然性较低,但比赛过程往往十分冗长。
本题中介绍的瑞士轮赛制,因最早使用于1895年在瑞士举办的国际象棋比赛而得名。它可以看作是淘汰赛与循环赛的折衷,既保证了比赛的稳定性,又能使赛程不至于过长。
2*N名编号为1~2N的选手共进行R轮比赛。每轮比赛开始前,以及所有比赛结束后,都会对选手进行一次排名。排名的依据是选手的总分。选手的总分为第一轮开始前的初始分数加上已参加过的所有比赛的得分和。总分相同的,约定编号较小的选手排名靠前。
每轮比赛的对阵安排与该轮比赛开始前的排名有关:第1名和第2名、第3名和第4名、……、第2K – 1名和第2K名、…… 、第2N – 1名和第2N名,各进行一场比赛。每场比赛胜者得1分,负者得0分。也就是说除了首轮以外,其它轮比赛的安排均不能事先确定,而是要取决于选手在之前比赛中的表现。
现给定每个选手的初始分数及其实力值,试计算在R轮比赛过后,排名第Q的选手编号是多少。我们假设选手的实力值两两不同,且每场比赛中实力值较高的总能获胜。
每次比赛之前进行一次排序即可
不使用sort进行排序
因为比赛前各位选手的分数都是有序的,所以,所有赢的选手组成的序列也是有序的,所有输的选手组成的序列也是有序的,那么,把选手再排序的时候就可以借助这两个有序数列进行,每次把两个有序数列之尾的分数较小的选手,放在选手序列的头部
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, r, q;
struct node {
int s, w, k;
}a[N];//选手序列
bool cmp(node x, node y) {
if(x.s == y.s)
return x.k < y.k;
return x.s > y.s;
}
node t1[N], t2[N];//两个辅助序列
int t1_r = 0;
int t2_r = 0;
int main() {
cin >> n >> r >> q;
n *= 2;
for(int i = 1; i <= n; i++) {
cin >> a[i].s;
a[i].k = i;
}
for(int i = 1; i <= n; i++) {
cin >> a[i].w;
}
sort(a + 1, a + 1 + n, cmp);
t1[0].s = t2[0].s = INT_MAX;//保证一个序列用完之后使用另一序列
for(int i = 1; i <= r; i++) {
for(int j = 1; j < n; j+= 2) {
if(a[j].w < a[j + 1].w) {
a[j + 1].s++;
t1[++t1_r] = a[j + 1];
t2[++t2_r] = a[j];
}
else {
a[j].s++;
t1[++t1_r] = a[j];
t2[++t2_r] = a[j + 1];
}
}
for(int j = n; j > 0; j--) {
if(t1[t1_r].s < t2[t2_r].s) {
a[j] = t1[t1_r--];
}
else if(t1[t1_r].s > t2[t2_r].s) {
a[j] = t2[t2_r--];
}
else {
if(t1[t1_r].k > t2[t2_r].k) {
a[j] = t1[t1_r--];
}
else {
a[j] = t2[t2_r--];
}
}
}
}
cout<<a[q].k;
return 0;
}