题目链接
题目描述
给定一个正整数数列,和正整数 p,设这个数列中的最大值是 M,最小值是 m,如果 M≤mp,则称这个数列是完美数列。
现在给定参数 p 和一些正整数,请你从中选择尽可能多的数构成一个完美数列。
输入格式:
输入第一行给出两个正整数 N 和 p,其中 N(≤10
5
)是输入的正整数的个数,p(≤10
9
)是给定的参数。第二行给出 N 个正整数,每个数不超过 10
9
。
输出格式:
在一行中输出最多可以选择多少个数可以用它们组成一个完美数列。
输入样例:
10 8
2 3 20 4 5 1 6 7 8 9
输出样例:
8
题目大意
不好概括,还是看题吧
解题思路
本题乍一看很唬人,像是一个动态规划
或者贪心算法
,但是只要好好读题就回发现,题目要求的是你从中选择尽可能多的数构成一个完美数列
而不是按照题目中所给的顺序选择一个完美数列
,这样我们就可以将给出的一组数按照从小到大排序后用双重循环来暴力解决
但是需要注意的是,本题的暴力虽说可以,但是对时间要求非常苛刻,必须完美的剪枝才能通过测试点4
- 测试点4的问题是完美剪枝,不仅内层循环要从上一次最长的地方开始循环,一不满足条件内层循环也必须直接
break
,否则会出现时间超限 - 测试点五的错误原因是用int类型会导致超限因为给出的范围虽然是
10^9
,但是由于要判断M
与n*p
的关系,两个数相乘就回超出int
的范围
具体的解题思路可以看下面题解的注释
题解
#include<bits/stdc++.h>
using namespace std;
vector<long long> v;
//这里是测地点5错误原因,包括下面的p也不能用int ,
// 因为给出的范围虽然是10^9,但是由于要判断M与n*p的关系,两个数相乘就回超出int的范围
int main(){
int n;
long long p;
//注意long long
cin>>n>>p;
v.resize(n);
for(int i=0;i<n;i++) cin>>v[i];
sort(v.begin(),v.end());
//因为题目中只是说从给出的数列中选出若干数字,并没有说必须按照给出的数列连续选出,所以可以排序后再判断
int mmax=0;
//用于记录当前最长的数组有几个元素,可以在内部循环中减少循环次数,降低时间复杂度
for(int i=0;i<v.size();i++){
for(int j=i+mmax;j<v.size();j++){
//这里直接从最大长度开始判断,可以降低不少次的循环次数
if(v[i]*p>=v[j]){
//这里的相乘就是测试点5超限答案错误的原因
mmax=j-i+1;
}
else break;
//这里不满足说明后面的循环都不满足了(因为前面已经将数列从小到大排好序了,后面只可能会更不满足题目条件)
//也需要跳出循环,本题时间复杂度卡的非常紧,暴力方法不剪枝也会超限
}
}
cout<<mmax;
}