这道题是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; }