给定一个正整数数列,和正整数 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
/*
分析:
对于一个完美数列,我们只需要知道最大值M和最小值m就可以了,在[m,M]这个范围内
数一定满足M<=mp,在一个数列中找完美数列,我们对这个数列进行升序排列,之后进行
操作就会方便一点,排序后:
1 2 3 4 5 6 7 8 9 20
这里排序我们还是选用qsort函数,多用用熟悉熟悉;
以m从左到右开始,找最大值,m=1,M=2,然后M逐个去找;
那么另外就有M从右到左开始,M=20,m=1,然后m逐个去找;
还有可能在中间的,这样的话情况就很复杂,再看题目输出,只需要输出最大完美数列
的个数,那么中间的状态我们可以不关心,我们只需要找下一个数列是否成立,利用已有
的完美数列最大个数;
比如:
1 2 3 4 5 6 7 8 9 20
p=3时;
我们用一个maxCount存放最大个数,最后输出,用一个thisCount作为当前的子列里个数;
maxCount=0;
这里m=1,让thisCount=1;因为这里左边m已经有了,每多一个我们进行相加;
M=2;2<=1*3;thisCount++;
M=3;3<=1*3;thisCount++;
M=4;4<=1*3不满足,结束m=1的这一段数列;比较maxCount与thisCount
发现maxCount小于thisCount,于是把thisCount的值赋给maxCount;然后找下一个;
m=2,maxCount=3,此时我们已经找到一个最大值了,于是小于等于3的值都没有意义了
因为结果输出的时最大子列的数的个数,小于3不输出,等于3也没必要更改,
于是M数到第4个
1 2 3 4 5 6 7 8 9 20
0 1 2 3 4 5 6 7 8 9
i 1 2 3 j
这里m=2,直接让j从第4个开始就可以了
j==thisCount+i+1==3+1+1==5;这里加1因为我们直接从thisCount的下一个开始
毕竟相等的时候不需要更新,前提知道m,只有在M之内,都满足M<=m*p;
M=6,6<=2*3,thisCount++;
M=7,7<=2*3不满足,更新maxCount,maxCount=thisCount;
然后m等于3,依次循环,直到j碰到数列边界就结束;输出maxCount;
p=4;
1 2 3 4 5 6 7 8 9 20
0 1 2 3 4 5 6 7 8 9 //初始:thisCount=0,maxCount=0;
i j //i=0,j=1成立->thisCount=1,maxCount=0;此时实际个数为2;
i j //i=0,j=2成立->thisCount=2,maxCount=0;此时实际个数为3;
i j //i=0,j=3成立->thisCount=3,maxCount=0;此时实际个数为4;
i j //i=0,j=4成立->thisCount=4,maxCount=0;此时实际个数为5;
i j //i=0,j=5不成立->thisCount=4,maxCount=4;此时实际个数为5;
i j //i=1,j=5成立->thsiCount=5,maxCount=4;此时实际个数为6;
i j //i=1,j=6成立->thisCount=6,maxCount=4;此时实际个数为7;
i j //i=1,j=7成立->thisCount=7,maxCount=4;此时实际个数为8;
i j //i=1,j=8不成立->thisCount=7,maxCount=7;此时实际个数为8;
i j //i=2,j=8成立->thisCount=7,maxCount=7;此时实际个数为8;
i j //i=2,j=9不成立->thisCount=7,maxCount7;此时实际个数为8;
这里我们发现当上一个i发生不成立时,j不应该从当前位置开始,应该从满足当前i与j之间个数为
thisCount的位置开始:
比如:
当i==0时,在j==5时发生M/m<=p不成立,此时thisCount为4,实际上是5,这里计数没有记录第一个数;
此时M的下标为1、2、3、4;
当i==1时,j=thisCount+i=5,但实际应该从j==6的位置开始,下标为2、3、4、5的一共4个;满足thisCount==4
如果此时j==6成立,那么thisCount++;在j==5的位置成立则会加重复,所以j应该等于i+thisCount+1;
这里thisCount记录的是最大值M的个数;输出的时候应该加上1;因为最小值m没有统计进来;
注意到第一次开始的时候,j==1==i+thisCount+1成立;
如果全都不成立,它自身也可以组成数列,自身的数列是一定的,即做最大值也做最小值;
注意这里每个数有可能为 1000000000,int型够用;
int的范围是-2147483648 - 2147483647
这里我们输入一些特殊数据进行debug
比如:
7 2
4 4 4 4 4 4
输出为0,然后去改代码;
*/
#include<iostream>
#include<cstdlib> //qsort函数包含在这个函数里;
//比较函数;
int compare(const void* x1,const void* x2){
if(*(int*)x1>=*(int*)x2){
return 1;
}
else {
return -1;
}
}
using namespace std;
int main(){
//输入N和p;
long long int N,p;
cin>>N>>p;
//输入数列;
long long int data[N];
for(int i=0;i<N;i++){
cin>>data[i];
}
//排序;
qsort(data,N,sizeof(long long int),compare);
//循环找maxCount;
int thisCount=0,maxCount=0;
int end=0; //end为0表示j未到边界,end为1表示j已经到达边界,无需继续判断;
int j=0;
for(int i=0;i<N;i++){ //这一层确定m;
for(int j=i+thisCount;j<N;j++){
if(data[j]<=p*data[i]){ //M<=m*p转化为M/m<=p;因为乘积可能超出整行范围;
thisCount++;
}
else{ //这时候就不满足了;//注意如果直到最后一个都满足的话,并没有更改maxCount;
if(maxCount<=thisCount){
maxCount=thisCount;
}
break; //不满足后保存最大值退出;
}
if(j==N-1){
maxCount=thisCount; //最后一个也满足,在这里更改maxCount;
end=1;
break; //抵达边界;
}
}
if(end==1){
break;
}
}
//输出最大个数;
cout<<maxCount<<endl;
return 0;
}
/*
反思:
这里第一次是把M<=m*p改为M/m<=p,这样可以用int型存放,并且全是正整数也不会出现除以零的情况;
但是测试点4通不过,答案错误;这里需要总结下除法与乘法之间的问题;
然后就是循环的边界,最开始没有第114行这个赋值语句,忽略了如果最后一个数也成立的话,那么不是
不成立的情况,因此没有更新maxCount的值,以后学代码多考虑各种情况,这里开始就是漏掉了最后一个
数成立的情况;然后就是thisCount的计数方式;之前我统计的是M的个数;也是可以的,因为这里我后面
M开始的位置是从m的位置开始,并非m的下一个,这样计数就是全部的数列数字个数,而非只有最大值;
像这一类题,不需要记录中间状态,只关心最后结果,就不用逐个求中间状态了,第一眼看到这个题,我就想
到最大子列和,和kmp算法这类,可以利用已有的结果优化算法,节省时间;
*/