P1309 [NOIP2011 普及组] 瑞士轮
原题链接:
这道题是NOIP2011年普及组的一道题
但是从考察内容和难度来说,还是一道挺值得做的好题
思路分析
题目知识点考到了排序,最朴素的想法当然是用快速排序,每一轮都进行一次快排,然后写完果然爆T了。。。
再重新思考,发现这道题中,winner组和loser组,内部是顺序不变的
这句话如何理解呢?所有的winner,在这轮比赛前,是按某个顺序排列的,那么此轮结束后,每个winner都+1分,那么他们的相对顺序是不变的
而loser组也同理,loser本轮并不得分,所以这一轮前后,loser内部顺序不发生改变
于是,我们可以用3个数组来计算(我多写了一个tmp,写归并留下的习惯了,其实直接用p数组就好)
先读入所有的参赛者,用C++ STL自带的sort()进行第一轮排序
然后模拟比赛,把每对参赛者的胜者放入winner组,败者放入loser组,再用归并排序中的归并操作,把他们重新放回原数组
重复上述操作$ r $次后,即完成比赛过程的模拟
最后直接输出p[q - 1](排名第Q位参赛者)的编号
相关知识点
sort() - STL内置排序函数,省去我们手写一个快排的功夫了
归并排序 - 题目中只用到了归并排序中的'归并'操作,但是理解这个算法还是很有意义的
只要掌握了快速排序和归并排序的思想以及代码,并且有解决普通模拟题的编码能力,应该是可以解决掉这道题的
代码实现
// P1309 瑞士轮
// 6 Mar 2021 - zqsml
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 2e5 + 10;
struct person{
int s, w, ind;
}p[N];
bool cmp(const person &a, const person &b){
if(a.s != b.s)
return a.s > b.s;
return a.ind < b.ind;
}
person winner[N], loser[N], tmp[N];
int n, r, q;
int main(){
scanf("%d %d %d", &n, &r, &q);
for(int i = 0; i < 2 * n; i ++ ) p[i].ind = i + 1;
for(int i = 0; i < 2 * n; i ++ ) scanf("%d", &p[i].s);
for(int i = 0; i < 2 * n; i ++ ) scanf("%d", &p[i].w);
sort(p, p + 2 * n, cmp);
while(r -- ){
for(int i = 0; i < n; i ++ ){
if(p[i * 2].w < p[i * 2 + 1].w){
p[i * 2 + 1].s ++ ;
loser[i] = p[i * 2];
winner[i] = p[i * 2 + 1];
}
else{
p[i * 2].s ++ ;
winner[i] = p[i * 2];
loser[i] = p[i * 2 + 1];
}
}
// p1 for winner, p2 for loser
int p1 = 0, p2 = 0;
int k = 0;
while(p1 < n && p2 < n){
if(winner[p1].s > loser[p2].s || (winner[p1].s == loser[p2].s && winner[p1].ind < loser[p2].ind)){
tmp[k ++ ] = winner[p1 ++ ];
}
else tmp[k ++ ] = loser[p2 ++ ];
}
while(p1 < n) tmp[k ++ ] = winner[p1 ++ ];
while(p2 < n) tmp[k ++ ] = loser[p2 ++ ];
for(int i = 0; i < 2 * n; i ++ ) p[i] = tmp[i];
}
printf("%d\n", p[q - 1].ind);
return 0;
}

浙公网安备 33010602011771号