这是一个值得仔细学习的题目。
给定一个正整数数列,和正整数 p,设这个数列中的最大值是 M,最小值是 m,如果 M≤mp,则称这个数列是完美数列。
现在给定参数 p 和一些正整数,请你从中选择尽可能多的数构成一个完美数列。
首先分析题目,要找完美数列,很自然可以想到排序,升序数列。
然后呢,要找一个大一个小,很显然需要利用two pointers思想,也就是从两头往中间查,找到尾部第一个V【j】满足大于V【i】*P即可,然后j-i就是所求,转化成求j-i的最大值,这个地方如果数组长度太长,很显然暴力求解是不太可能的,必须要用到二分查找。
algorithm头文件下的upper_bound和lower_bound是写好的二分查找函数
原型如下:
upper_bound(begin,end,num) // 返回第一个大于m的数组下标(vector迭代器)找不到就返回数组长度N和迭代器V.end()
lower_bound(begin,end,num) // 与upper不同的是,这个返回第一个大于等于的元素下标或vector迭代器。
若寻找小于或小于等于,需要使用如下方法:
lower_bound( begin,end,num,greater</type/>() ):
从数组的begin位置到end-1位置二分查找第一个小于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num,greater</type/>()
从数组的begin位置到end-1位置二分查找第一个小于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
使用greater()需要引用头文件 functional
使用二分查找写的代码如下:
需要注意的是,最后一个测试点运算会超过int,这也是令人意想不到的错误,在刷题中尤其需要注意,如相加相乘都要考虑到溢出问题。
#include<iostream>
#include<vector>
#include<algorithm>
#include<functional>
using namespace std;
int main() {
int N, P, max = 0, tmp;
scanf("%d%d", &N,&P);
vector<int>V(N);
for (int i = 0; i < N; i++) {
scanf("%d", &V[i]);
}
sort(V.begin(), V.end());
for (int i = 0; i < N; i++) {
auto it = upper_bound(V.begin() + i, V.end(), (long long)V[i] * P);
tmp = it - (V.begin() + i);
if (tmp > max)max = tmp;
}
printf("%d", max);
return 0;
}
第二个方法是一种高明的做法,具体有多高明一看就知道了。
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int main() {
int N, M, res = 0, tmp;
scanf("%d %d", &N, &M);
vector<int>V(N);
for (int i = 0; i < N; i++) {
scanf("%d", &V[i]);
}
sort(V.begin(), V.end());
for (int i = 0; i < N; i++) {
for (int j = i + res; j < N; j++) {
if (V[j] <= (long long)V[i] * M) {
tmp = j - i + 1;
if (tmp > res)res = tmp;
}
else break; //如果不加break,效率大打折扣。
}
}
printf("%d", res);
return 0;
}