UESTC 1339 郭大侠与线上游戏

题目地址  http://acm.uestc.edu.cn/#/problem/show/1339

这道题,是求中位数,以前也有一道中位数的题目卡的我要死,这次这道题我依旧没有办法自己做出来,找了题解才明白怎么做,但是现在才开始分析为什么要这样做。

我一开始的想法很简单,直接用一个vector来模拟这题目,但是排序很耗费时间,一共一百万组操作,我就假设我里面五十万个数字,然后一直快排,就达到3e8的运算量了。所以会TLE。

我们要压缩这个复杂度,我们发现中位数的定义,就是n个数字排序后的第n/2+1个,我们要做的就是维护他就行了。

1,所以我们开两个set,一个L一个U,L里面放小于中位数的,U里面放大于等于中位数的,增加数字,判断加入L还是U,并且如果U的元素个数比L的元素个数多2,就把U里面最小的放入L中并删除。

2,删除操作也是这样。也是学到新东西了,删除后比如L有2个元素,U有1个,那么我们可以用rbegin来把L里面最大的元素放入U中,即U.insert(L.rbegin()),当然,自己开迭代器找好位置来赋值也是可以的。

3,删除的时候我们是要删除队列里面的第一个元素,那么我们先从第一个数t从队列里面取出来,判断下他在L还是在U,然后删除嘛,但是set容器中find的复杂度是O(n)的,所以用find还过不了,所以我们应该用L.lower_bound(t),这样就可以知道他的位置了。

4,set的size是unsignedint类型,如果相减是负数那么就会变成很大的数字,所以第53行的错误一开始没找出来。所以尽量遵循能加就不减,能乘就不除吧。

5,分析一下这种方法复杂度,代码里面最大的处理常数是10(进入ope1,然后进入else,然后进入if),对于这道题目一百万的处理,也就是最多也才到一千万的处理(其实还有set里面增加减少是logn的次数没有考虑),不考虑常数(不卡常)其实就是维护中位数,近似于O(n)的复杂度,这样还不能过那就太丧心病狂了。相比于上面是n * nlogn快了不知道多少倍。

代码如下:

#include<set>
#include<iostream>
#include<queue>
#include<cstdio>
using namespace std;
int main()
{
	int n, ope, t;
	set <int> l, u;
	set <int>::iterator iter;
	queue <int> q;
	cin >> n;
	while(n--)
	{
		scanf("%d", &ope);
		if(ope == 1)
		{
			scanf("%d", &t);
			q.push(t);
			if(u.size() == 0 || t >= *u.begin())
			{
				if(l.size() == u.size())
					u.insert(t);
				else
				{
					u.insert(t);
					l.insert(*u.begin());
					u.erase(u.begin());
				}
			}
			else
			{
				if(l.size() == u.size())
				{
					l.insert(t);
					iter = l.end();
					iter--;
					u.insert(*iter);
					l.erase(iter);
				}
				else
					l.insert(t);
			}
		}
		else if(ope == 2)
		{
			t = q.front();
			q.pop();
			if(t < *u.begin())
				l.erase(l.lower_bound(t));
			else
				u.erase(u.lower_bound(t));
			if(u.size() > l.size() + 1)
			{
				l.insert(*u.begin());
				u.erase(u.begin());
			}
			else if(l.size() > u.size())
			{
				iter = l.end();
				iter--;
				u.insert(*l.rbegin());
				l.erase(iter);
			}
		}
		else
			printf("%d\n", *u.begin());
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值