2

题目大意:

从N个正整数中选择若干个数,使得选出的这些数中的最大值不超过最小值的p倍。问满足条件的选择方案中,序列最长的长度是多少。

算法思路:

从N个正整数中选择若干个数字组成一个序列,最大值为M,最小值为m,那么该序列中的数字,也就是从m到M的数字一定是原序列中的连续序列,因为不然就会存在一个数字大于m小于M不在选择的序列中,然后将其加入到该序列使其长度变大并且符合题意,那么原来选出的序列就不是最长的了。所以我们一定是在一个有序序列中选出一个连续序列,使其最大值不超过最小值的p倍。为此,使用nums数组存储输入序列,然后将该序列进行排序,在有序序列中进行查找,这里给出2种查找的方式。

算法实现1:

使用二分查找,这里借助upper_bound实现,该函数可以找到第一个大于待查找元素的位置pos,如果不存在返回数组长度,那么对于满足mp>=M的序列长度就为pos减去当前遍历元素的位置。然后每次都使用ans记录更大的长度。在pos为N的时候就没有必要继续往后查询了,因为一定都满足mp>=M,并且其长度一定小于当前的序列长度

算法实现2:

使用2个指针i和j,初始都指向0,让i指向的元素为m,让j一值向后走,知道第一次遇见nums[i]p<nums[j]时停止,在j走动的过程中不断更新其长度(使用ans保存)然后在j停止后让i向后走一格,j接着之前的位置向后继续走(没有从0开始向后走),因为m增大的时候,小于j位置的元素都满足nums[i]p>=该元素。同样的在nums[i]*p大于序列中最大的元素max_num的时候就没有必要查询j了。

二分查找的函数说明:
1、lower_bound(起始地址,结束地址,要查找的数值) 返回的是数值第一个大于等于待查找数字出现的位置。
2、upper_bound(起始地址,结束地址,要查找的数值) 返回的是 第一个大于待查找数字出现的位置。
3、binary_search(起始地址,结束地址,要查找的数值)  返回的是是否存在这么一个数,是一个bool值

提交结果:

截屏2020-10-16 下午2.55.56.png

AC代码1:

#include <cstdio>
#include <algorithm>

using namespace std;

int main(){
    int N,p;
    scanf("%d %d",&N,&p);
    long long nums[N];
    for (int i = 0; i < N; ++i) {
        scanf("%lld",&nums[i]);
    }
    sort(nums,nums+N);
    long long ans = -1;
    for (int j = 0; j < N; ++j) {
        long long m = nums[j];
        // 找到第一个大于m*p的元素位置,前一个位置就是满足m*p>=M的位置,并且该序列的长度最长
        long long pos = upper_bound(nums+j,nums+N,m*p)-nums;
        long long len = pos-j;
        ans = ans>len?ans:len;
        if(pos==N){
            // 从j到N-1的所有的元素都满足m*p>=M,无需再遍历后续元素了,其长度一定比当前短
            break;
        }

    }
    printf("%lld",ans);
    return 0;
}

AC代码2:

#include <cstdio>
#include <algorithm>

using namespace std;

int main(){
    int N,p;
    scanf("%d %d",&N,&p);
    long long nums[N];
    long long max_num = -1;
    for (int i = 0; i < N; ++i) {
        scanf("%lld",&nums[i]);
        max_num = max_num<nums[i]?nums[i]:max_num;
    }
    sort(nums,nums+N);
    long long ans = -1;
    int j=0;
    for (int i=0;i<N;++i) {
        if(nums[i]*p>=max_num) {
            //nums[i]*p大于序列中最大的元素max_num,无需再往后遍历了。
            ans = ans>N-i?ans:N-i;
            break;
        }
        while (j<N){
            if(nums[i]*p<nums[j]){
                //找到第一个大于m*p的元素
                break;
            }
            ans = ans>j-i+1?ans:j-i+1;
            ++j;
        }
    }
    printf("%lld",ans);
    return 0;
}

乔梓鑫
569 声望17 粉丝

主要分享个人学习经验和心得