1. 程式人生 > 其它 >大一第三次月賽“序列中找數”題解

大一第三次月賽“序列中找數”題解

  • 題目描述

    おんなのこ gives you a permutation PER and a positive integer K.(PER is a permutation of (1,2,3,.....,N)).

    For each i = K,K+1,......,N,find the K-th greatest value among the first i terms of PER.

  • 大致意思
    對每個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;
    }