题目大意:
从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值。
提交结果:
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;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。