1030 完美数列 (25 point(s))
给定一个正整数数列,和正整数 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
题意:有n个元素,元素可组成多个数列,如果某数列的最小值,题目给出的某个值p的积,大于或等于该数列中的最大值,则说明该数列是完美数列,求这些完美数列最长能多长;
0 1 2 3 4 5 6 7(该列数对应下面数,为数组下标)
如: 数列 1 2 3 4 5 6 7 8就是一个完美数列
min=1,p=8,8=1*8;所以该数列的长度即7-0+1,len=8;
代码:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main(){
int n;
double p;//不用把vector也定义为double类型,因为double类型和int类型的积为double类型,所以定义p为double就行了;
cin>>n>>p;
vector<int >v(n);
for(int i=0;i<n;i++){
cin>>v[i];
}
sort(v.begin(),v.end());//从小到大排序,为下面服务;
int len=0,templen=0;
for(int i=0;i<n;i++){
for(int j=i+len;j<n;j++){//为什么是i+len,因为是从小到大排序,前面数列的元素一定能进入后面的数列,作为后面数列的元素,比如,第一个数列的最小值为1,1*p=8,则len为8,最大数为8,那么当min=2的时候,2*p=16,16》8,所以1所在的数列的元素一定能被2为最小值的数列容纳,所以只用从最小值的下标i+len开始就好了;
if(v[j]<=v[i]*p){
templen=j-i+1;//求出最大值和最小值之前的距离,题意所据的例子len=7-0+1=8;
if(templen>len){
len=templen;//因为是求最长长度,更新len的值;
}
}else {
break;
}
}
}
cout<<len;
return 0;
}
二分算法求解(upper_bound)//找到第一个大于x的值的位置,该数列的长度就等于该位置,因为这个位置是数组的下标嘛,下标数比长度小一,所以>=的数列刚好就是这个下标,找出这个下标就可以知道这个数列的长度了;
代码:
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int a[100010],n,m;//函数调用数组,全局定义;
int binarySearch(int i,long long x){
if(a[n-1]<=x) return n;//序列内的数都满足的情况,此时减首项的0得到得不是首尾得长度,如4-0等于4,但a[4]到a[0]应该有5个数,所以就不适用了就应该是return n;
int l=i,r=n-1,mid;
while(l<r){
mid=(l+r)/2;
if(a[mid]>x) {//第一个大于x的值的位置;
r=mid;
}else {
l=mid+1;
}
}
return l;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
sort(a,a+n);
int maxx=0;
for(int i=0;i<n;i++){
int low=binarySearch(i,(long long)a[i]*m);//测试点5不过得原因是因为溢出;
//不能long long x=a[i]*m,乘出来已经溢出了,再转换成long long 没有意义,(long long)这种写法可以再乘之前把变量强制转换成Longlong类型就不会溢出了;
maxx=max(maxx,low-i);//是low-i,把数列的长度算出来,low只是第一个大于x的下标而已,减去起点就得到长度了;
}
printf("%d",maxx);
return 0;
}
直接调用模板库:
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int a[100010];
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
sort(a,a+n);
int maxx=0;
for(int i=0;i<n;i++){
int low=upper_bound(a+i+1,a+n,(long long) a[i]*m)-a;//upper_bound返回的是*int 类型
//要转换成整型就要减掉首地址a;upper_bound(begin,end,x)
maxx=max(maxx,low-i);
}
printf("%d",maxx);
return 0;
}
二指针的写法:(控制两个变量在数组内移动,两指针夹出来的这部分就满足题意,为了满足这个条件,i在不满足的时候向右边移动,j向左边移动)
two point 代码:
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
int n,p;
cin>>n>>p;
const int maxn=100010;
int a[maxn]={0};
for(int i=0;i<n;i++){
cin>>a[i];
}
sort(a,a+n);
int i=0,j=0,cnt=0;
while(i<=j){//控制i右移;
while(a[j]<=(long long )a[i]*p&&j<n){//控制j左移动;
j++;
cnt=max(cnt,j-i);//更新cnt的值,j-1就是夹出来的那部分的数组的长度,该部分满足情况;
}
i++;
}
cout<<cnt;
return 0;
}
注意不能用两个for循环来控制指针,要用上面的这种写法,不然测试点4超时;
如:
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
int n,p,i,j;
const int maxn=100010;
int a[maxn]={0};
scanf("%d %d",&n,&p);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
sort(a,a+n);
int num=0;
for(i=0;i<n;i++){
for(j=i;j<n;j++){
long long tempnum=(long long )a[i]*p;
if(tempnum>=a[j]){
continue;
}else {
break;
}
}
int temp=j-i;
num=max(num,temp);
}
printf("%d",num);
return 0;
}