洛谷P1309

在写本题前要搞清楚几个问题:

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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值