给定一个正整数数列,和正整数 p,设这个数列中的最大值是 M,最小值是 m,如果 M≤mp,则称这个数列是完美数列。
现在给定参数 p 和一些正整数,请你从中选择尽可能多的数构成一个完美数列。
输入格式:
输入第一行给出两个正整数 N 和 p,其中 N(≤105)是输入的正整数的个数,p(≤109)是给定的参数。第二行给出 N 个正整数,每个数不超过 109。
输出格式:
在一行中输出最多可以选择多少个数可以用它们组成一个完美数列。
输入样例:
10 8
2 3 20 4 5 1 6 7 8 9
输出样例:
8
1、理解题意
给定参数 p 和一些正整数,设其中的最大值是 M,最小值是 m,如果 M≤mp,则称这个数列是完美数列,在一行中输出最多可以选择多少个数可以用它们组成一个完美数列。
2、解题思路
(1)若count[j]<=count[i]*p,那么对[i,j]区间内的任意位置k,一定有count[k]<=count[i]*p成立,这种有序序列的特性引导我们向two pointers方向考虑;
(2)令i,j的初值为0,c记录满足条件的最大长度。从第一个元素开始,让j不断加1,直到不等式count[j]<=count[i]*p不成立为止;
让i向右移一位,继续让j在上次位置基础上不断加1(i向右移,count[i]变大,可以取到的count[j]必然大于等于上一轮得到的count[j]),以此类推,直到j到达序列最右端。(时间复杂度为O(n))。
代码如下:
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100010;
int main(){
int n;
int p;
scanf("%d %d",&n,&p);
int m,M;
int count[maxn];
for(int i=0;i<n;i++){
scanf("%d",&count[i]);
}
sort(count,count+n);
int i=0,c=0,j=0;
while( i<n&&j<n){
while(j<n&&count[j]<=(long long)count[i]*p){
c=max(c,j-i+1);
j++;
}
i++;
}
printf("%d\n",c);
return 0;
}
3、总结
1、本题采用two pointers(二分法)。two pointers是利用有序序列的枚举特性来有效降低复杂度的一种非常重要的算法思想,它利用问题本身与序列的特性,使用两个下标i、j(i和j一般互相牵制)对序列进行扫描(可以同向扫描,也可以反向扫描),以较低的复杂度(一般为O(n))解决问题。
2、本题若采用暴力求解(使用二重循环),时间复杂度为O(n2),对n在105的规模时是不可承受的,所以提交后会显示“运行超时”。
3、two pointers通常使用while循环。
4、由于m<=109,p<=109,那么m*p很有可能为1018,超过了整型范围,应进行强制类型转换,转换成long long型。