洛谷排序–瑞士轮(归并排序)
题目背景:
在双人对决的竞技性比赛,如乒乓球、羽毛球、国际象棋中,最常见的赛制是淘汰赛和循环赛。前者的特点是比赛场数少,每场都紧张刺激,但偶然性较高。后者的特点是较为公平,偶然性较低,但比赛过程往往十分冗长。
本题中介绍的瑞士轮赛制,因最早使用于1895年在瑞士举办的国际象棋比赛而得名。它可以看作是淘汰赛与循环赛的折中,既保证了比赛的稳定性,又能使赛程不至于过长。
题目描述:
2×N 名编号为 1N∼2N 的选手共进行R 轮比赛。每轮比赛开始前,以及所有比赛结束后,都会按照总分从高到低对选手进行一次排名。选手的总分为第一轮开始前的初始分数加上已参加过的所有比赛的得分和。总分相同的,约定编号较小的选手排名靠前。
每轮比赛的对阵安排与该轮比赛开始前的排名有关:第1名和第2 名、第 3 名和第 4名、……、第2K −1名和第2K名、…… 、第2N −1名和第2N名,各进行一场比赛。每场比赛胜者得11分,负者得 00分。也就是说除了首轮以外,其它轮比赛的安排均不能事先确定,而是要取决于选手在之前比赛中的表现。
现给定每个选手的初始分数及其实力值,试计算在R 轮比赛过后,排名第Q的选手编号是多少。我们假设选手的实力值两两不同,且每场比赛中实力值较高的总能获胜。
输入输出格式
输入格式:
第一行是三个正整数N,R ,Q每两个数之间用一个空格隔开,表示有 2×N名选手、R 轮比赛,以及我们关心的名次 Q。
第二行是2 ×N 个非负整数s1, s2, …,s2N,每两个数之间用一个空格隔开,其中si表示编号为i的选手的初始分数。 第三行是2 N 个正整数w1 , w2 ,…,w2N,每两个数之间用一个空格隔开,其中 wi 表示编号为i 的选手的实力值。
输出格式:
一个整数,即R轮比赛结束后,排名第Q 的选手的编号。
输入输出样例
输入样例#1:
24276671052015 2 4 2 7 6 6 7 10 5 20 15 24276671052015
输出样例#1:
1 1 1
说明
解题思路:
直接模拟,将每一次的比分都算出来,然后排序进行下一次的排序,但是调用sort函数直接超时,因为sort函数在全部乱序的时候会占优势,但是在相对有序的情况下,采用归并排序所需要的时间要少些。针对这道题,每一轮的输家和赢家相对有序的,所以,需要将每一轮的赢家排序和输家排序即可,之后归并则可以知道下一次比较的位次和分数情况。关于大佬的瑞士轮的题解如下链接,说一句,他的个人主页真的太好看了。
#include <iostream>
#include <algorithm>
using namespace std;
struct Participant
{
int index;
int score;
int ability;
};
//index, score, ability
Participant participants[200001];
Participant winners[100001];
Participant losers[100001];
bool cmp(const Participant &p1, const Participant &p2)
{
if (p1.score != p2.score)
return p1.score > p2.score;
else
return p1.index < p2.index;
};
int main()
{
ios::sync_with_stdio(false);
int n, r, q;
while (cin >> n >> r >> q)
{
//输入选手的初始得分和序号
for (int i = 1; i <= 2 * n; ++i)
{
cin >> participants[i].score;
participants[i].index = i;
}
//输入选手的能力值
for (int i = 1; i <= 2 * n; ++i)
cin >> participants[i].ability;
//按照分数从大到小排序
sort(participants + 1, participants + 2 * n + 1, cmp);
//R 轮比赛的模拟
for (int i = 0; i < r; ++i)
{
//每相邻两人都比一次
for (int j = 1; j < 2 * n; j += 2)
{
int winner = participants[j].ability > participants[j + 1].ability ? j : j + 1;
int loser = participants[j].ability < participants[j + 1].ability ? j : j + 1;
//分别把赢家和输家放入到不同的序列中
//可以证明 winnders 和 losers 一定是有序的
winners[(j + 1) / 2] = participants[winner];
losers[(j + 1) / 2] = participants[loser];
winners[(j + 1) / 2].score++;
}
//合并有序序列到 participants
merge(
winners + 1, winners + n + 1,
losers + 1, losers + n + 1,
participants + 1, cmp
);
}
cout << participants[q].index << endl;
}
return 0;
}
这里在关于大佬使用了ios::sync_with_stdio(false);不懂意思特别去百度了,原来去掉了同步的功能。cin慢是有原因的,其实默认的时候,cin与stdin总是保持同步的,也就是说这两种方法可以混用,而不必担心文件指针混乱,同时cout和stdout也一样,两者混用不会输出顺序错乱。正因为这个兼容性的特性,导致cin有许多额外的开销,如何禁用这个特性呢?只需一个语句std::ios::sync_with_stdio(false);,这样就可以取消cin于stdin的同步了。程序如下:
给大家链接,一定要去看https://blog.youkuaiyun.com/qq_33248299/article/details/52144485
归并算法了解下一下
上面题目使用的是算法头文件里面的归并算法,小编尽职尽责的还是把里面的头文件弄出来给大家看看吧
sort;
for i = 1 : R with step = 1
{
for (pair in participants)
compete;
modify score in participants;
sort;
}
复杂度为 O(nlogn)+R×(O(N)+O(nlogn))O(nlogn)+R×(O(N)+O(nlogn))
然后:
sort;
for i = 1 : R with step = 1
{
for (pair in participants)
compete;
split to winners + losers;
merge(winners, losers) to participants
}
复杂度为 O(nlogn)+R×(O(N)+O(N))O(nlogn)+R×(O(N)+O(N))。
时间复杂度一下子降了好多!至于空间复杂度。。。谁在乎呢!
那么怎么 merge 呢?我们当然可以自己手写,不过最好的办法是直接用 std::merge
,它就在 <algorithm>
头文件里。
template <class InputIterator1, class InputIterator2, class OutputIterator, class Compare>
OutputIterator merge (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2,
OutputIterator result, Compare comp);
意思是:把 [first1, last1)
和 [first2, last2)
合并到 result
开头的空间里,最后一个参数是比较器。
当然,关于归并排序的写法,自己手写的话,推荐个网址,老师讲的好清楚的
真希望自己有一天也能成为这样的大佬。
下面给大家展示视频里面的源代码吧
//归并算法
#include<stdio.h>
void merge(int arr[],int L,int M,int R){
int LEFT_SIZE=M-L;
int RIGHT_SIZE=R-M+1;
int left[LEFT_SIZE];
int right[RIGHT_SIZE];
int i;
//1.fill in the left sub array
for(i=L;i<M;i++){
left[i-L]=arr[i];
}
//2.fill in the right sub array
for(i=M;i<=R;i++){
right[i-M]=arr[i];
}
//3.meger into original array
i=0;int j=0;int k=L;
while(i<LEFT_SIZE&&j<RIGHT_SIZE){
if(left[i]<right[i]){
arr[k]=left[i];
i++;
k++;
}
else{
arr[k]=right[j];
j++;
k++;
}
}
while(i<LEFT_SIZE){
arr[k]=left[i];
i++;
k++;
}
while(j<RIGHT_SIZE){
arr[k]=right[j];
j++;
k++;
}
}
void mergeSort(int arr[],int L,int R){
if(L==R){
return;
}
else{
int M=(L+R)/2;
mergeSort(arr,L,M);
mergeSort(arr,M+1,R);
merge(arr,L,M+1,R) ;
}
}
int main(){
int arr[]={2,1,20,15,4,3,6,7};
int L=0;
int R=7;
int M=4;
int i;
//merge(arr,L,M,R);
mergeSort(arr,L,R);
for(i=0;i<=R;i++){
printf("%d\n",arr[i]);
}
return 0;
}
ge(arr,L,M+1,R) ;
}
}
int main(){
int arr[]={2,1,20,15,4,3,6,7};
int L=0;
int R=7;
int M=4;
int i;
//merge(arr,L,M,R);
mergeSort(arr,L,R);
for(i=0;i<=R;i++){
printf("%d\n",arr[i]);
}
return 0;
}
期待下一篇的DFS吧