本人数据结构小白,感谢大家的支持!
目录
来自AtCoder Beginner Contest 253的C题
Time Limit: 2 sec / Memory Limit: 1024 MB
Problem Statement
We have a multiset of integers , which is initially empty.
Given queries, process them in order. Each query is of one of the following types.
-
1 x
: Insert an into . -
2 x c
: Remove an from times, where -
3
: Print . It is guaranteed that SS is not empty when this query is given.
Constraints
- When a query of type
3
is given, is not empty. - All values in input are integers.
Output
Print the answers for the queries of type 3
in the given order, separated by newlines.
Sample Input 1
8
1 3
1 2
3
1 2
1 7
3
2 2 3
3
Sample Output 1
1
5
4
Tips 1
Sample Input 2
4
1 10000
1 1000
2 100 3
1 10
分析及代码实现
题目大意是对一个初始为空的容器S进行操作,输入指令,包括1:添加一个元素x(输入),2:删除指定元素x m次(删除m个x元素,这里m为c(输入)和x在容器中的个数的最小值,3:输出最大值和最小值之差。
话不多说,直接按题意开码!由于python列表有count和remove方法,暴力法如下:
python brute-force
n=eval(input())
ls=[]
for i in range(n):
od=list(input().split())
if od[0]=='1':
ls.append(eval(od[1]))
elif od[0]=='2':
x=eval(od[1])
t=ls.count(x)
c=min(eval(od[2]),t)
for j in range(c):
ls.remove(x)
elif od[0]=='3':
print(max(ls)-min(ls))
理所当然,TLE了。看来用顺序存储结构来做效率太低了,如果删除指定元素需要的时间复杂度。并且像上面一样进行2:删除元素查询x在列表中出现的元素从而算出m又需要进行线性查找。
分析后发现并不需要按照题目的定义算出这个m,如果输入的c大于中的个数,只需将从中全部删去即可。
为了实现更快的添加和删除元素,考虑使用哈希表进行操作。哈希表中对元素操作的时间复杂度为。一下就比stl中的vector和python中的list快很多了吧。可以用python中的字典或c++ stl中的unordered_map实现。在使用unordered_map
时,需要引入头文件:unordered_map,并不是用#include <map>就可以了。
由于查询3要求出最大最小值,而unordered_map是无序的并且没有max_element和min_element方法,每次求最值又需要进行线性查找,必须得进行优化。
下面使用python实现哈希表的做法:
创建一个字典,key为需要添加到容器中的元素x,value为元素x出现的次数
考虑存储当前最值,每添加一个新的元素,就更新最大最小值,进行删除操作时还有剩余元素时可以不考虑最值变化,只需更新元素剩余数量,那当键值对被删除后怎么做呢?
这时候我们再用python的max,min函数对字典的键求最值进行更新,能比每次进行查询3:线性查找减少不少时间。还要考虑容器被删空的情况,这时要初始化maxs和mins的值。
python-dictionary
s={}
maxs,mins=0,int(1e9+1)
for _ in range(int(input())):
od=list(map(int,input().split()))
if len(od)==1: #使用转化为列表后长度判断查询类型,对应查询3
# print(max(s.keys())-min(s.keys())) 直接用max和min进行线性查找会TLE
print(maxs-mins)
elif len(od)==2: #对应查询1
if od[1] in s:
s[od[1]]+=1 #值加一
else:
s[od[1]]=1 #创建键值对
#这样写还可能会超时,可以不用系统函数
# 条件判断od[1]>max od[1]<min再更新
#min,max函数中上述条件取等号会增加指令执行次数
maxs,mins=max(maxs,od[1]),min(mins,od[1]) #更新最值
elif len(od)==3: #对应查询2
if od[1] in s:
s[od[1]]-=od[2]
if s[od[1]]<=0: #删除键值对
s.pop(od[1])
if len(s)==0:
maxs,mins = 0,int(1e9+1)
else: #优化后只需在这里线性查找
maxs, mins = max(s.keys()), min(s.keys())
这个优化需要用两个变量存储实时最值,因为哈希表元素是无序的,如果用有序的容器来存储,虽然效率比不上哈希表,但是求最值的过程比上面要快且简便。
map是STL中的一个关联容器,和python中的数据类型字典一样,map 类型变量中的元素也是由键-值对组成,没有重复的键,其底层实现是红黑树,最重要的是map中的元素实时都是有序的。map中的key和value是一个pair结构中的两个分量,可以使用下标的方式插入数据。
value默认是0,所以可以这样写:mp[x]+=1
使用erase方法可删除数据,erase(iterator)删除迭代器指向的元素,erase(key)删除键值对
由于是有序的,最大值就是迭代器rbegin指向的元素,最小值就是begin指向的元素
stl-map
#include<iostream>
#include<map>
using namespace std;
int main()
{
int q;
cin>>q;
map<int,int> mp;
while (q--)
{
int a;
cin>>a;
if (a==1)
{
int x;
cin>>x;
mp[x]+=1;
}
else if (a==2)
{
int x,c;
cin>>x>>c;
mp[x]-=c;
if (mp[x]<=0)
mp.erase(x);
}
else if (a==3)
{
cout<<(*mp.rbegin()).first-(*mp.begin()).first<<endl;//注意运算符的优先级
}
}
return 0;
}
还可以用multiset来实现。(嘿嘿,原题在描述这个容器时用的就是multiset),方法和map差不多
引入头文件:#include <set>
set中一个元素只能出现一次,而multiset可以存放多个相同元素,和map一样是有序的
使用insert插入元素 ,删除元素erase方法和map相同,但map方法我们存放的是键值对(值x对应的pair元素只有一个,用value来判断这个值出现的次数或者说是否存在),multiset是单个题目中要求的元素,所以用multiset更加直观,删除时也要删除多次。
stl-multiset
#include<iostream>
#include<set>
using namespace std;
int main()
{
int q;
cin>>q;
multiset<int> ms;
while (q--)
{
int a;
cin>>a;
if (a==1)
{
int x;
cin>>x;
ms.insert(x);
}
else if (a==2)
{
int x,c;
cin>>x>>c;
multiset<int>::iterator it=ms.find(x);
while (c--&&it!=ms.end()) //如果所有的x都已经删除it就指向end
{
ms.erase(it);
it=ms.find(x);
}
}
else if (a==3)
{
cout<<(*ms.rbegin()-*ms.begin())<<endl;
}
}
return 0;
}
变式题目(multiset/priority_queue)
其实我在比赛中遇到这个问题时并不会stl中的map和multiset QAQ,但我想到了之前遇到的另外一道题,这题当时我也不会做,后面得知可用优先队列。AtCoder这道题就像是下面这道题的升级版
回头过来看,这题也可以用multiset来做,或者说priority_queue能做的multiset都能做
#include<iostream>
#include<set>
using namespace std;
int main()
{
int q;
cin>>q;
multiset <int> s;
while (q--)
{
char od;
cin>>od;
if (od=='i')
{
int t;
cin>>t;
s.insert(t);
}
else if (od=='q')
{
cout<<*s.begin()<<endl;
s.erase(s.begin());
}
}
return 0;
}
原来的优先队列解法
#include<iostream>
#include<queue>
using namespace std;
int main()
{
int n;
cin>>n;
priority_queue <int,vector<int>,greater<int> > q;
while (n--)
{
char od;
cin>>od;
if (od=='i')
{
int t;
cin>>t;
q.push(t);
}
else if (od=='q')
{
cout<<q.top()<<endl;
q.pop();
}
}
return 0;
}