萌新学习算法——归并排序进阶(一)
通过上面对归并排序的学习,接下来我们通过一道洛谷的一道题目来深化对归并排序的了解:
题目P1309 瑞士轮
题目描述
2×N 名编号为 1∼2N 的选手共进行R 轮比赛。每轮比赛开始前,以及所有比赛结束后,都会按照总分从高到低对选手进行一次排名。选手的总分为第一轮开始前的初始分数加上已参加过的所有比赛的得分和。总分相同的,约定编号较小的选手排名靠前。
每轮比赛的对阵安排与该轮比赛开始前的排名有关:第1 名和第2 名、第 3 名和第 4名、……、第2K - 1名和第2K名、…… 、第2N - 1名和第2N名,各进行一场比赛。每场比赛胜者得1分,负者得 0分。也就是说除了首轮以外,其它轮比赛的安排均不能事先确定,而是要取决于选手在之前比赛中的表现。
现给定每个选手的初始分数及其实力值,试计算在R 轮比赛过后,排名第Q的选手编号是多少。我们假设选手的实力值两两不同,且每场比赛中实力值较高的总能获胜。
题目分析:
首先我们不用归并排序来解决这道问题。我们来分析一下,该题目是要排名来进行比较的,也就是说第一名和第二名,第三名和第四名…第2n-1和第2n名进行比赛,并且以能力值大的赢得比赛,并且分数加一,当然输的加零。对了还有一点要注意的,我们还要控制当分数一样时,编号在前面的先输出,接下来我们看一下代码实现。
我们先定义一个保存选手的类Person。
Person
class Person{
int id,power,score; //id保存选手的编号,power保存选手的实力值,score保存取得分数
public Person(int id, int power, int score) {
this.id = id;
this.power = power;
this.score = score;
}
}
接着我们来重写一下比较器Comparator建一个sortScore的方法
sortScore
class sortScore implements Comparator<Person>
{
public int compare(Person p1, Person p2) {
if(p1.score==p2.score)
return p1.id-p2.id;
return p2.score-p1.score;
}
}
完整代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Comparator;
class Person{
int id,power,score; //id保存选手的编号,power保存选手的实力值,score保存取得分数
public Person(int id, int power, int score) {
this.id = id;
this.power = power;
this.score = score;
}
}
class sortScore implements Comparator<Person>
{
public int compare(Person p1, Person p2) {
if(p1.score==p2.score)
return p1.id-p2.id; //当分数一样的时候,id小的排在前面
return p2.score-p1.score; //当分数不一样的时候,分数大的在前面
}
}
public class Main {
public static int n,r,q;
public static Person persons[];
public static void main(String[] args) throws IOException {
BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
String str[]=reader.readLine().split(" ");
n=Integer.parseInt(str[0]);r=Integer.parseInt(str[1]);q=Integer.parseInt(str[2]);
str=reader.readLine().split(" ");
String str2[]=reader.readLine().split(" ");
persons=new Person[2*n];
for(int i=0;i<2*n;i++)
persons[i]=new Person(i, Integer.parseInt(str2[i]), Integer.parseInt(str[i]));
//对选手进行第一次从大到小的排序
Arrays.sort(persons,new sortScore());
for(int j=0;j<r;j++)
{
for(int i=0;i<2*n;i+=2)
{
if(persons[i].power>persons[i+1].power)
persons[i].score++;
else
persons[i+1].score++;
}
Arrays.sort(persons,new sortScore());
}
System.out.println(persons[q-1].id+1);
}
}
运行结果
我们会发现爆了四个点,这是为什么呢?我们翻阅API获得原来Arrays的sort方法相当于快排,好了这里说到快速排序,什么是快速排序?快速排序是和归并排序一样运用分治法,他每一次都会确定一个位置,但是对于这道题目,哪里不适用呢?我们会发现这道题基本是有序的,但是按照快排来做的话,我们会发现每次都要确定所有的位置才能完成排序,但是在这里,我们并不需要,我们的排序是基本有序的。这里提到一个知识点,相信你们都听说快排相对来说最优的排序,但是他是在序列基本无序的时候才成立的,但是对于基本有序的,我们通常要通过归并排序来做,进而减少不必要的过程。代码实现如下:
前序工作:
定义:
public static Person persons[],temp[];
public static Person win[], lose[];// 用来保存赢的数组,和输的数组
Merger算法:
public static void Meger()
{
int i=0,j=0,k=0;
while(i<n&&j<n) {
//分数大的在前面
if(win[i].score>lose[j].score)
temp[k++]=win[i++];
else
if((win[i].score<lose[j].score))
temp[k++]=lose[j++];
else //单分数相同时,要比较编号,编号小的在前面
{
if(win[i].id>lose[j].id)
temp[k++]=lose[j++];
else
temp[k++]=win[i++];
}
}
//将剩下的直接加入队尾
while(i<n)
temp[k++]=win[i++];
while(j<n)
temp[k++]=lose[j++];
//逐一赋值
for(i=0;i<2*n;i++)
persons[i]=temp[i];
}
完整代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Comparator;
class Person {
int id, power, score; // id保存选手的编号,power保存选手的实力值,score保存取得分数
public Person(int id, int power, int score) {
this.id = id;
this.power = power;
this.score = score;
}
}
class sortScore implements Comparator<Person> {
public int compare(Person p1, Person p2) {
if (p1.score == p2.score)
return p1.id - p2.id; // 当分数一样的时候,id小的排在前面
return p2.score - p1.score; // 当分数不一样的时候,分数大的在前面
}
}
public class Main {
public static int n, r, q;
public static Person persons[],temp[];
public static Person win[], lose[];// 用来保存赢的数组,和输的数组
public static void Meger()
{
int i=0,j=0,k=0;
while(i<n&&j<n) {
//分数大的在前面
if(win[i].score>lose[j].score)
temp[k++]=win[i++];
else
if((win[i].score<lose[j].score))
temp[k++]=lose[j++];
else //单分数相同时,要比较编号,编号小的在前面
{
if(win[i].id>lose[j].id)
temp[k++]=lose[j++];
else
temp[k++]=win[i++];
}
}
//将剩下的直接加入队尾
while(i<n)
temp[k++]=win[i++];
while(j<n)
temp[k++]=lose[j++];
//逐一赋值
for(i=0;i<2*n;i++)
persons[i]=temp[i];
}
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String str[] = reader.readLine().split(" ");
n = Integer.parseInt(str[0]);
r = Integer.parseInt(str[1]);
q = Integer.parseInt(str[2]);
str = reader.readLine().split(" ");
String str2[] = reader.readLine().split(" ");
persons = new Person[2 * n];
temp = new Person[2 * n];
win = new Person[n];
lose = new Person[n];
for (int i = 0; i < 2 * n; i++)
persons[i] = new Person(i, Integer.parseInt(str2[i]), Integer.parseInt(str[i]));
// 对选手进行第一次从大到小的排序
Arrays.sort(persons, new sortScore());
for (int j = 0; j < r; j++) {
for (int i = 0; i < 2 * n; i += 2) {
if (persons[i].power > persons[i + 1].power) {
persons[i].score++;
win[i / 2] = persons[i];
lose[i / 2] = persons[i + 1];
} else {
persons[i + 1].score++;
win[i / 2] = persons[i+1];
lose[i / 2] = persons[i];
}
}
Meger();
}
// for(int i=0;i<2*n;i++)
// System.out.println(persons[i].id+" "+persons[i].power+" "+persons[i].score);
System.out.println(persons[q - 1].id + 1);
}
}