大一第三次月赛“序列中找数”题解
- 题目描述
おんなのこ gives you a permutation PER and a positive integer K.(PER is a permutation of (1,2,3,.....,N)).
- 大致意思
对每个i(i = K,K=1,....,N)在序列PER 前 i 项中 找出第K个大的数 - 解题步骤
首先当i是K的时候,那么根据题目意思,在序列前K项中找第K个大的数。。。那么这个数是不是前K项中最小的那个数?
然后当i是K+1的时候,根据题意,在序列前K项中找第K个大的数。。那么这个数就是在前K项数的集合中,加入第K+1这个项后的第二个大的数,最小的数可以扔掉了
也就是说要考虑这个K+1项是否比前K项数的最小的那个数还要小。如果比前K项数的最小还要小,那么最终找的数还是之前的前K项中最小的那个数,反之,就把之前的前K项集合中最小的数去掉,
把第K+1项加入集合再进行排序,选取新的集合最小的值作为答案
依次类推。。。直到推到i是N的时候,都可以把每次更新的集合的最小数作为每次新增加序列元素的选取答案 - 数据范围
K和N都是1e5次方级别,如果每次选取最小值之前,都对那个集合手动排序的话,时间复杂度是O(NKlogK),级别O(n2),显然已经超时了,那么及考虑有没有什么数据类型可以自动排序呢,
那么就想到优先队列和set集合都可以自动排序, 优先队列的本质是堆,那么进行插入更新操作最后总的时间复杂度大概为O(nlogn),set的本质是红黑二叉树,set集合的插入更新操作最后的时间复杂度也是O(nlogn),都不会超出时间范围 - 示例代码1(优先队列)
#include <iostream> #include <cstdio> #include <string> #include <queue> using namespace std; int main() { priority_queue<int,vector<int>,greater<int> > q; int n,k; cin>>n>>k; int h; for(int i=0;i<k;i++){ cin>>h; q.push(h); } cout<<q.top()<<endl; for(int i=k;i<n;i++){ cin>>h; if(h>q.top()){ q.pop(); q.push(h); } cout<<q.top()<<endl; } // return 0; } - 示例代码2(set集合)
#include <iostream> #include <cstdio> #include <set> using namespace std; int main() { int n,k; cin>>n>>k; set<int> se; for(int i=0;i<k;i++){ int h; cin>>h; se.insert(h); } cout<<*se.begin()<<endl; for(int i=k;i<n;i++){ int h; cin>>h; if(h>*se.begin()){ se.erase(*se.begin()); se.insert(h); } cout<<*se.begin()<<endl; } return 0; } - 示例代码3(手动排序超时做法)
//不使用STL,,,TLE #include <iostream> #include <cstdio> #include <algorithm> using namespace std; int per[500005]; int main() { int n,k; //cin>>n>>k; scanf("%d%d",&n,&k); for(int i=0;i<n;i++){ //cin>>per[i]; scanf("%d",&per[i]); } sort(per,per+k,greater<int>());//序列per中前k个数进行从大到小排序 //cout<<per[k-1]<<endl; printf("%d\n",per[k-1]); int temp; for(int i=k;i<n;i++){ int j=i; while(1){ if(per[j]<per[j-1]||j==0){ break; } temp=per[j]; per[j]=per[j-1]; per[j-1]=temp; j--; } //cout<<per[k-1]<<endl; printf("%d\n",per[k-1]); } return 0; }

浙公网安备 33010602011771号