目录
A - The Balance of the World
题目链接:HRBU 2021年暑期训练Day2 - Virtual Judge
题意:给予你一个字符串,以 . 表示输入结束,要求判断字符串中所有的()以及 [ ] 是否全都都是成对出现的,严格要求顺序,即( 必须出现在 ) 之前
题目思路:简单的括号匹配问题,无非就是判断两种情况
1、当 )出现时是否已经出现过(,如果出现了,则将两者进行抵消操作
2、当 [ 出现时是否已经出现过 ] ,如果出现了,也将两者进行抵消
STL中的栈(stack)就能很完美的完成这一操作。如果不太了解栈这个函数,还请移步这个博客
博客链接:C++ STL 详解_Five—菜鸟级的博客-CSDN博客_c++stl详解
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxx=1e7+10; const int maxn=2e5+10; int main() { string s; while(true) { getline(cin,s); if(s==".") return 0; int flag=1; stack<char> st; for(int i=0; i<s.size()&&flag; i++) { if(s[i] == '(' || s[i] == '[') { st.push(s[i]); } else if(s[i]==')') { if(st.empty()||st.top()!='(') flag=0; else st.pop(); } else if(s[i]==']') { if(st.empty()||st.top()!='[') flag=0; else st.pop(); } } if(!st.empty()) flag=0; if(flag) cout<<"yes"<<endl; else cout<<"no"<<endl; } return 0; } /* */
B - Stones
题目链接:HRBU 2021年暑期训练Day2 - Virtual Judge
题意:一个男银,因为自行车坏了,所以他要从东边走到西边去。走路的途中实在是太无聊了,所以,这个男银打起了路上石头的主意(脑子可能不太对),他决定在自己遇上奇数块的石头时,就把这个石头扔出去,偶数块的时候就不扔了,当这个男银不能在扔石头时,就说明他走到头了,那么问,石头从开始位置(第一块石头的位置)到结束位置(不能在扔石头时碰上的石头的位置)最长是多少?输入提供所有的石头位置以及能扔出去的距离。
题目特殊说明要是两块石头或者多块石头出现在同一位置上,则优先碰上能扔的距离最小的那个石头
题目思路:循坏遍历同一个数组,对于奇数次碰上石头,就把这个石头扔出去,再把这个落下的位置重新记入数组里(需要排序的,因为人物是一步步移动,也就是+1,所以要对于新数据插入后排序),若是偶数的话,就跳过。正常的数组模拟的话,单单是数组插入重新排序这一操作就会直接T,所以我们选择优先队列!优先队列的性质就是对插入的数据会重新进行排序,保证队列里的数据一定是有序的。注意还有一个题目特殊说明的地方,多块石头同一位置时,优先碰上抛出距离小的那个。我们就要重载一下优先队列的排序方式来满足题目条件了,优先队列的学习也可以参考A题中的那个博客(里边啥都有)
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxx=1e7+10; const int maxn=1e5+10; struct Node { int p; int s; friend bool operator <(const Node &a,const Node &b) { if(a.p==b.p) return a.s>b.s; return a.p>b.p; } }; int main() { int T,N; cin>>T; while(T--) { priority_queue<Node> Q; cin>>N; Node x; for(int i=1;i<=N;i++) { cin>>x.p>>x.s; Q.push(x); } int flag=1; while(!Q.empty()) { x=Q.top(); Q.pop(); if(flag==1) { x.p+=x.s; Q.push(x); } flag*=-1; } cout<<x.p<<endl; } return 0; } /* */
C - Running Median
题目链接:HRBU 2021年暑期训练Day2 - Virtual Judge
题意:提供了一个数组,要求在奇数次记录当前数组中的中位数,当输入结束时要求输出这么一个数组,里面包含所有奇数次记录下的中位数
题目思路:精简的题目往往更加恐怖(QAQ吓人)在输入的同时进行数据对比,求出中位数。这用C根本实现不出来嘛,这不欺负老实人吗!(你要是会当我没说)
这里咱们引入一个新的想法
对顶堆
就是这样的两个堆,当我们输入数据时以小根堆的堆顶(或者大根堆的堆顶)作为分界点,小于的就加到小根堆里,大于时就加入到大根堆里,等于时就具体看以哪边为分界点就加入哪边。同时我们还需要保证两边堆里的数据个数相差不能大于1个,因为对顶堆的作用就是用来寻找中位数的,要是你两个堆中数据个数相差都大于1个了,那么无法保证你的数据可靠性,因此我们需要把个数更多的堆里的数据塞到另一个堆里。那么这个时候我们就只需要在输入个数为奇数时,去截取你作为分界点的那个堆的堆顶即是此时这一组数据中的中位数啦
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxx=1e7+10; const int maxn=1e5+10; int main() { int N,T,M; cin>>T; while(T--) { priority_queue<int> B; priority_queue<int,vector<int>,greater<int> > S; int cnt=0,a,ans[maxn]; cin>>N>>M; for(int i=1;i<=M;i++) { cin>>a; if(B.empty()) B.push(a); else { if(a>B.top()) S.push(a); else B.push(a); } while(B.size()<S.size()) { int t=S.top(); S.pop(); B.push(t); } while(S.size()<B.size()-1) { int t=B.top(); B.pop(); S.push(t); } if(i&1) ans[++cnt]=B.top(); } cout<<N<<" "<<(M+1)/2<<endl; for(int i=1;i<=cnt;i++) { if(i%10==1) cout<<ans[i]; else if(i%10==0) cout<<" "<<ans[i]<<endl; else cout<<" "<<ans[i]; } cout<<endl; } return 0; } /* 4+3+2+1=10 */
D - Let the Balloon Rise
题目链接:HRBU 2021年暑期训练Day2 - Virtual Judge
题意:给你所有的气球的颜色,需要你判断输出数量最多的气球的颜色
题目思路:把每一个碰上的气球颜色都记录下来,然后判断一下是否见过,见过的话就在原有的数量基础上+1,要是没见过就开创一个新的位置。map yyds,STLmap的简单运用,注意map遍历时使用的是迭代器,需要学习一下,详情参考A题的博客链接
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxx=1e7+10; const int maxn=1e5+10; int main() { int n; while(cin>>n) { if(n==0) break; string b,a; map<string,int> mp; for(int i=1;i<=n;i++) { cin>>b; mp[b]++; } int maxx=0; map<string,int>::iterator it; for(it=mp.begin();it!=mp.end();++it) { if(it->second>maxx) { maxx=it->second; a=it->first; } } cout<<a<<endl; } return 0; } /* */
E - Shopping
题目链接:HRBU 2021年暑期训练Day2 - Virtual Judge
题意:给你好几个店名以及每一天的每家店的收入变化,要求输出memory这家店每一天的排名是多少?
题目思路:对于每一家店每一天的状态都进行记录,然后依次判断即可,使用map容器。
我的做法就是这样,还有一种想法(没去实现,但是感觉是可以的),就是multimap中会按照key值进行一个排序,那么我们是不是可以按照每一天的收入去输入这个数据,接着按照multimap的性质从大到小的遍历输出是不是也可以满足条件呢,multimap能以一个key值对应多个value值,所以不必担心数据会重复这个问题
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxx=1e7+10; const int maxn=1e5+10; int main() { int n,m; while(cin>>n) { string b; map<string,int> mp; for(int i=1; i<=n; i++) { cin>>b; mp[b]=0; } cin>>m; for(int i=1; i<=m; i++) { for(int j=1;j<=n;j++) { int c; cin>>c>>b; mp[b]+=c; } map<string,int>::iterator it=mp.begin(); int num,cnt=1; for(it; it!=mp.end(); it++) { if(it->first=="memory") num=it->second; } for(it=mp.begin(); it!=mp.end(); it++) { if(it->second>num&&it->first!="memory") cnt++; } cout<<cnt<<endl; } } return 0; } /* */
F - pairs
题目链接:HRBU 2021年暑期训练Day2 - Virtual Judge
题意:题目提供n个数据,要求你算出有多少对数据满足,两个数据之间的差的绝对值小于k。
题目思路:我们需要判断某一个数与其他数据之间的差的绝对值,这不就是以这个数为中点,差值为半径的一段线段吗
图示
这一段红色区域不就表示我们要求的一个区间,因此我们用x+k减去x-k不就是满足条件的数字个数
做法:把输入的数据进行排序,之后对数据进行一个查找,查找数组中存在于当前这个数字+k,-k的区间中的个数,然后进行答案的累加,最后输出。因为数据量是100000,所以我们常规的查找在时间复杂度上是不满足的,因此调用STL中的二分查找lower_bound函数以及upper_bound函数。函数的具体介绍我就不叨叨了,可以借鉴以下博客进行学习
博客链接:关于lower_bound( )和upper_bound( )的常见用法 - 莫莫酱 - 博客园
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxx=1e7+10; const int maxn=1e5+10; int main() { int T,n,k,P[maxn]; cin>>T; while(T--) { ll ans=0; cin>>n>>k; for(int i=1;i<=n;i++) cin>>P[i]; sort(P+1,P+n+1); for(int i=1;i<=n;i++) { int l=lower_bound(P+1+i,P+1+n,P[i]-k)-P-1; int r=upper_bound(P+1+i,P+1+n,P[i]+k)-P-1; ans+=r-l; } cout<<ans<<endl; } return 0; } /* */
G - 产生冠军
题目链接:HRBU 2021年暑期训练Day2 - Virtual Judge
题意:自己看题面,英文我还能给你翻译,中文自己想去
题目思路:要求判断是否有冠军产生,这是不是意味着总人数和败者组的差值刚好等于1,表示为所有人中有一个人一次都没有输,那他不就是冠军了?因此用set的去重性质,存储两个个数,一个是总人数,一个是败者组,然后判断是不是相差1就行。关于set的介绍,也在A题的链接里哦!
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxx=1e7+10; const int maxn=2e5+10; int main() { int n; while(cin>>n) { if(n==0) break; string win,los; set<string> W,L; for(int i=1;i<=n;i++) { cin>>win>>los; W.insert(win); W.insert(los); L.insert(los); } if(W.size()-L.size()==1) cout<<"Yes"<<endl; else cout<<"No"<<endl; } return 0; } /* */
H - Kiki & Little Kiki 1
题目链接:HRBU 2021年暑期训练Day2 - Virtual Judge
题意:模拟容器操作,两种操作 1、把数据存进去 2、把一个数据输出,条件是输出的数字是不大于输入的这个数据的最大整数,存在就输出数字,不存在就输出"No Element!"
题目思路:在每一次要求输出的操作时,我们都要进行数据的一次判断,找出最大的小于当前这个数字的整数,模拟这个操作需要极高的时间复杂度(啥操作模拟都搞时间复杂度,除了你好世界)我们可以调用upper_bound函数来查找第一个大于该数字的数字,然后判断一下这个数据前是否还存在数据即可,不存在说明没有数据了,存在则输出前一个数据就行。对于这个数据的操作,推荐一个新的容器multiset,具有数据可重复的性质,也可以调用upper_bound函数来进行数据的查找
upper_bound函数介绍博客链接:关于lower_bound( )和upper_bound( )的常见用法 - 莫莫酱 - 博客园
multiset容器介绍博客链接:C++ STL 详解_Five—菜鸟级的博客-CSDN博客_c++stl详解
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxx=1e7+10; const int maxn=1e5+10; int main() { int T,n,k,P[maxn]; string s; while(cin>>n) { multiset<int> mset; multiset<int>::iterator it1; for(int i=1; i<=n; i++) { cin>>s>>k; if(s=="Push") mset.insert(k); else { it1=mset.upper_bound(k); if(mset.empty()||it1==mset.begin()) { cout<<"No Element!"<<endl; } else { it1--; cout<<*it1<<endl; mset.erase(it1); } } } cout<<endl; } return 0; } /* 4+3+2+1=10 */