zoukankan      html  css  js  c++  java
  • 1085 Perfect Sequence (25 分)

    题意

    从N个正整数中选择若千个数,使得选出的这些数中的最大值不超过最小值的p倍。问满足条件的选择方案中,选出的数的最大个数。

    思路

    由于题干中涉及序列的最大值和最小值,因此不妨先将所有N个正整数从小到大进行排序。在此基础上证明:能使选出的数个数最大的方案,一定是在该递增序列中选择连续的若千个数的方案。

    证明:
    设递增序列A为({a_1,a_2, cdots ,a_i,a_{i+1}, cdots ,a_{i+m}, cdots ,a_j, cdots ,a_n}),假设从中能选出的个数最大的方案为({a_i,a_{i+1}, cdots ,a_{i+m},a_j}),即(a_i,a_{i+1}, cdots ,a_{i+m})为序列中连续的数,(a_j)为与其在序列中不连续的数。因此该方案中的最大值为(a_j),最小值为(a_i),且满足(a_j le a_i * p)。而事实上,由于原序列A中元素是递增的,因此可以把方案扩充为连续序列(a_i,a_{i+1},cdots,a_j),而这个序列的最大值仍然为(a_j),最小值仍然为(a_i),因此(a_j le a_i * p)仍然成立。于是就找到了一个新的序列,使得它的长度比原先不连续序列的长度要长。因此假设不成立,得出结论:能使选出的数的个数最大的方案,一定是在该递增序列中选择连续的若千个数的方案。证毕。

    于是问题就转换成:在一个给定的递增序列中,确定一个左端点a[i]和一个右端点a[j],使得(a[j] le a[i] * p)成立,且j-i最大。

    如果强制进行(O(n^2))的二重循环枚举,那么根据题目的数据范围,肯定是会超时的。这里有两种方法来解决这个问题:二分查找和two pointers。

    从左至右扫描序列,对其中的每一个数a[i],在a[i+1] ~ a[n-1]内二分查找第一个超过a[i] * p的数的位置j,这样j-i就是对位置i来说满足(a[j] le a[i] * p)的最远长度。取所有j-i的最大值即为所求的答案,时间复杂度为(O(logn))

    代码

    二分:

    const int N=1e5+10;
    int a[N];
    int n,p;
    
    int main()
    {
        cin>>n>>p;
    
        for(int i=0;i<n;i++) cin>>a[i];
        
        sort(a,a+n);
    
        int ans=0;
        for(int i=0;i<n;i++)
        {
            int j=upper_bound(a+i,a+n,(LL)a[i]*p)-a;
            ans=max(ans,j-i);
        }
    
        cout<<ans<<endl;
    
        //system("pause");
        return 0;
    }
    

    首先,可以很容易地获得下面这个性质:如果a[j] ≤ a[i] * p成立,那么对[i, j]内的任意位置k,一定有a[k] ≤ a[i] * p也成立。这种有序序列的性质就引导我们往two pointers思想去考虑,由此可以得到以下时间复杂度为(O(n))的算法。

    令两个下标i、j的初值均为0,表示i、j均指向有序序列的第一一个元素,并设置计数器count存放满足a[j] ≤ a[i] * p的最大长度。接下来让j不断增加,直到不等式a[j] ≤ a[i] * p恰好不成立为止(在此过程中更新count)。之后让下标i右移一位,并继续上面让j不断增加的操作,以此类推,直到j到达序列末端。这个操作的目的在于,在a[j] ≤ a[i] * p的条件下始终控制i和j的距离最大。

    双指针:

    const int N=1e5+10;
    int a[N];
    int n,p;
    
    int main()
    {
        cin>>n>>p;
    
        for(int i=0;i<n;i++) cin>>a[i];
        
        sort(a,a+n);
    
        int ans=0;
        int j=0;
        for(int i=0;i<n;i++)
        {
            while(j<n && a[j] <= (LL)a[i]*p) j++;
            ans=max(ans,j-i);
        }
    
        cout<<ans<<endl;
    
        //system("pause");
        return 0;
    }
    
  • 相关阅读:
    OpenAL播放pcm或wav数据流-windows/ios/android(一)
    Windows录音API学习笔记--转
    Windows基础-实时录音程序(WaveXXX)
    Windows基础-使用XAudio2播放音频(本质是WASAPI)
    XAudio2播放PCM
    jps的用法及常见问题介绍
    eureka添加security验证之后,client注册失败
    Maven中央仓库地址大全,Maven中央仓库配置示例
    Maven入门指南:仓库
    Maven中央仓库地址
  • 原文地址:https://www.cnblogs.com/fxh0707/p/14415686.html
Copyright © 2011-2022 走看看