PAT-B 1030. 完美数列(25)

题目链接在此

本题目前有二分查找法、two pointers法。

题意

从N个正整数中选出若干个数,使得选出的这些数中的最大值M和最小值m满足M<=m*p。问满足条件的选择方案中,选出的数的最大个数。

二分查找法

思路

首先需要对N个正整数从小到大排序,然后需要明白,选出的数个数最大的方案,一定是在该递增序列中连续的若干序列(这里不做证明,很好想)。

首先,很容易想到的是从左往右扫描,对于每个a[i],在a[i+1]~a[n-1]中找到第一个大于a[i]*p的数的位置index,这样index-i就是满足条件的最远长度。

但是这样两遍线性扫描的时间复杂度太大,所以需要将在a[i+1]~a[n-1]找位置换成二分查找。

注意点:
1. 数据范围会超过int,索性都用long long
2. 当数组a[]中所有数都超过了a[i]*p的情况进行特判

AC代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>

    using namespace std;

    long long n, a[100010], p;

    int main(){

        scanf("%lld %lld",&n, &p);

        for(int i = 0; i < n; i++){
            scanf("%lld",&a[i]);
        } 

        sort(a,a+n); //从小到大排序

        int ans = 1;

        int i;
        int left, right;
        for(i = 0; i < n; i++){
            long long mp = a[i]*p; //mp 对应题干m*p 
            left = i+1, right = n-1;

            if(a[right] < mp){ //如果数组中最大的数都小于mp,则从i到最后一个数都满足 
                right = n;
            }else{
                while( left < right){ //二分查找 
                    int mid = (left+right)/2;
                    if(a[mid] <= mp){ //a[mid]<=要找的数,则说明第一个大于要找的数在mid的右边 
                        left = mid+1;
                    }else{
                        right = mid;    
                    }
                }
            }
            ans = max(ans,right-i);
        } 

        printf("%d\n",ans);

        return 0;
    }

two pointers法

思路

首先,很容易得到:如果a[M]<=a[m]*p成立,那么对于[m,M]内的任意位置k,一定也有a[k]<=a[m]*p。这种有序序列的性质就引导我们往two pointers思想去思考:

首先对输入进行从小到大的排序,然后令两个下标m,M,都从0开始,并设置计数器count保存满足a[M]<=a[m]*p的最大长度。
接下来让M不断增加,知道不等式a[M]<=a[m]*p不成立为止,在此过程中更新count。之后让m右移一位,比继续上面让j不断增加的操作,依次类推,直到M到达序列末端。

AC代码

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

int n,p;
long long a[100010];

int main(){

    scanf("%d %d",&n,&p);

    for(int i = 0; i < n; i++){
        scanf("%lld",&a[i]);
    }

    sort(a,a+n);

    int m = 0,M = 0;
    long long sum ;
    int count = 1;
    while( m < n){
        sum = a[m]*p;
        while(a[M] <= sum && M < n){
            count = max(count, M-m+1);
            M++;
        }   
        m++;
    }

    printf("%d\n",count);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值