题目粗心:
从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;}