在写本题前要搞清楚几个问题:
1.sort函数是以快速排序为基础的
2.快速排序和归并排序的区别是快速排序主要用于无序数列,而归并排序是可以用于有序数列的。就像两个有序数列合并时用归并排序最好。
3.提示:虽然本题我没有用STL,但是STL库中确实是有sort和merge两个排序的。
来分析本题:
本题是一道非常好的题,可以帮助我们更深的理解快速排序和归并排序的本质。
通常思路是,用结构体储存数据,在不同队伍进行比赛时,每一轮比赛都使用一次快速排序,最后输出我们需要的答案
如下列代码
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 1e5 + 10;
//typedef long long LL;
int n, q, r;
struct node {
int s, w, na;
}p[2 * N];
bool cmp(node a, node b)
{
if (a.s == b.s)
return a.na < b.na;
return a.s > b.s;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> r >> q;
for (int i = 1; i <= 2 * n; ++i)
{
p[i].na = i;
cin >> p[i].s;
}
for (int i = 1; i <= 2 * n; ++i)
cin >> p[i].w;
sort(p + 1, p + 2 * n + 1, cmp);
for (int i = 1; i <= r; ++i)
{
for (int j = 1; j <= 2 * n; j += 2)
{
if (p[j].w < p[j + 1].w)
p[j + 1].s++;
else if (p[j].w > p[j + 1].w)
p[j].s++;
else
continue;
}
sort(p + 1, p + 2 * n + 1, cmp);
}
cout << p[q].na << endl;
return 0;
}
但是这样的方法会有4个点TLE,因为快排的时间复杂度时O(nlog2n),当数据非常大时,所需要的时间会非常长。
再回到题目中,我们注意到:
每一轮比赛后,胜者全部+1,单论胜者而言还是有序的
同理,负者全部不加分,单论负者而言也都是有序的。
这是我们能想到什么?
没错!就是归并排序
归并排序的本质就是分治递归,先把数组分割成单独的一份,而每一份都可以看作有序的,然后在一层一层归并,在每一层归并时都会进行排序。
单纯使用快速排序会浪费很多时间,因为第一次排序后胜者组和败者组都有序了,这样的话快排的优势反而成了劣势。
而我们可以分成胜者组和败者组两组,这两组都算是有序的,这样就省去了前面的分割,直接进行归并。
先送上归并排序的代码(内涵逆序数的求法)
//归并排序
#include <bits/stdc++.h>
//逆序对:例如(9,8)为一个逆序对
//逆序数:逆序对的数量
using namespace std;
const int N = 1e4 + 10;
int tmp[N];//辅助数组
int cnt;//逆序对
void arry_add(int arry[], int left, int mid, int right)
{
if (left >= right)return;
int i = left, j = mid + 1, k = 0;//k是tmp数组的下标
while (i <= mid && j <= right)//使用辅助数组tmp将分开的两个数组进行归并同时进行排序。当然,这是完整的归并排序,在一些情况下也不需要辅助数组。
{
if (arry[i] <= arry[j])
{
tmp[k++] = arry[i++];
}
else
{
tmp[k++] = arry[j++];//逆序对:(9,8)为一组逆序对
cnt += (mid - i + 1);//逆序数(逆序对的数量)的求法
}
}
//因为有时候某一个数组可能就已经是有顺序的了,所以上面的while循环不一定能把所有的数组都放进去,这时候下面的这两个while循环就是用来把剩下的数放进tmp数组
while (i <= mid)
tmp[k++] = arry[i++];//因为k是tmp数组的下标,所以才能使tmp数组有序。
while (j <= right)
tmp[k++] = arry[j++];
for (int i = 0; i < k; i++)
arry[i + left] = tmp[i];//最后把tmp数组的有序数列都复制给相应的arry数组(这时候不一定是全部的arry数组,可能只是一部分所以要i+left)
}
void merge_sort(int arry[], int left, int right)//一个递归函数
{
if (left >= right)return;
int mid = (left + right) >> 1;
merge_sort(arry, left, mid);//先把arry数组全部分开,分成最小的一份
merge_sort(arry, mid + 1, right);
arry_add(arry, left, mid, right);//然后一层一层进行归并
}
int main()
{
int num[] = {9,8,7,6,5,4,3,2,1 };
merge_sort(num, 0, 8);
cout << "排序后的数组: ";
for (int i = 0; i < 9; ++i)
cout << num[i] << " ";
cout << endl;
cout << "逆序对: " << cnt << endl;
return 0;
}
最后送上AC代码:
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 1e5 + 10;
//typedef long long LL;
int n,q,r;
struct node{
int s, w, na;
}p[2*N], w[N], l[N];
bool cmp(node a, node b)
{
if (a.s == b.s)
return a.na < b.na;
return a.s > b.s;
}
void merge_sort(node arr1[], node arr2[])//归并排序
{//在这个里面我们不需要分割,只需要归并即可。
int i =1 , j =1, k = 1;
while (i <= n && j <= n)
{
if (arr1[i].s > arr2[j].s || (arr1[i].s == arr2[j].s && arr1[i].na < arr2[j].na))
{
p[k] = arr1[i];//只有一层归并,所以这里没有用到辅助数组,直接用原数组p进行存储,反正下次也会更新的
//p[k].na = arr1[i].na;
//p[k].w = arr1[i].w;
k++;
i++;
}
else
{
p[k] = arr2[j];
//p[k].na = arr2[j].na;
//p[k].w = arr2[j].w;
k++;
j++;
}
}
while (i <= n)
{
p[k] = arr1[i];
//p[k].na = arr1[i].na;
//p[k].w = arr1[i].w;
k++;
i++;
}
while (j <= n)
{
p[k] = arr2[j];
//p[k].na = arr2[j].na;
//p[k].w = arr2[j].w;
k++;
j++;
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> r >> q;
for (int i = 1; i <= 2 * n; ++i)
{
p[i].na = i;
cin >> p[i].s;
}
for (int i = 1; i <= 2 * n; ++i)
cin >> p[i].w;
sort(p + 1, p + 2 * n + 1, cmp);
for (int i = 1; i <= r; ++i)
{
for (int j = 1, k=1; j <= 2 * n; j += 2, k++)
{
if (p[j].w < p[j + 1].w)
{
w[k].s = p[j + 1].s + 1;
w[k].na = p[j + 1].na;
w[k].w = p[j + 1].w;
l[k] = p[j];
}
else if (p[j].w > p[j + 1].w)
{
w[k].s = p[j].s + 1;
w[k].na = p[j].na;
w[k].w = p[j].w;
l[k] = p[j + 1];
}
}
merge_sort(w, l);
}
cout << p[q].na << endl;
return 0;
}