1085 Perfect Sequence (25分)
Given a sequence of positive integers and another positive integer p. The sequence is said to be a perfect sequence if M≤m×p where M and m are the maximum and minimum numbers in the sequence, respectively.
Now given a sequence and a parameter p, you are supposed to find from the sequence as many numbers as possible to form a perfect subsequence.
Input Specification:
Each input file contains one test case. For each case, the first line contains two positive integers N and p, where N (≤105) is the number of integers in the sequence, and p (≤109) is the parameter. In the second line there are N positive integers, each is no greater than 109.
Output Specification:
For each test case, print in one line the maximum number of integers that can be chosen to form a perfect subsequence.
Sample Input:
10 8
2 3 20 4 5 1 6 7 8 9
Sample Output:
8
题目意思就是 给你两个数,一个是n 一个是p,然后给你n个数字,从这n个数中选出尽可能多的数,满足 最大值<=最小值*p
测试点1:我推测 是考察 数列中最大值和最小值是一个数
思路:
第一步:肯定要先用sort对n个数字排序
第二步:想当然的是使用两层循环,但时间肯定爆炸,所以需要优化一下。
第一层的循环是确定最大值,第二层循环就需要考虑一下了,如果第二层循环从前向后,其实就是最小值越来越小的过程,也就说最开始循环时候的最小值 不满足 最大值<=最小值*p ,后面的就不可能满足
然后还有一个加速的小技巧就是,假如我们运行到一半了 求得满足条件的序列长度是5,记为len,那么我们下一次循环,完全可以直接查找Len个位置之后 的数字,看是否满足
#include <iostream>
using namespace std;
#include <set>
#include <algorithm>
#include <vector>
bool cmp(int a,int b)
{
return a>b;
}
int main()
{
long long n,p;
cin>>n>>p;
//vector<long long> sn(n);
long long sn[n];
for(int i=0; i<n; i++)
{
cin>>sn[i];
}
sort(sn,sn+n,cmp);
int len=1;
for(int i=0;i<n;i++){
long long M=sn[i];
//这里加上len是再次加快循环速度,必须要加上,否则还是超时
for(int pos=i+len-1;pos<n&&M<=sn[pos]*p;pos++){
len=pos-i+1;
}
}
cout<<len;
}
第二个方法就是 二分法,对于这种循环找满足条件的都完美适用。
第一个循环的变量 i 视为每次我们所找序列的最大值的位置,然后寻找满足条件的最小值(所谓满足条件就是说 这个值 刚好是 i这个最大值 所能找到的最长序列,比如 4 3 2 1 然后P=2,最初的i是 0 就是说我们从最大值为4 开始寻找满足的序列,第二次循环i=1 就是说我们从最大值为3 开始寻找满足的序列),那么我们在找最长序列的最小值的时候 起始位置就是i(最大值和最小值可以使一个数) 结束位置就是n-1,其实和暴力的双层循环解决该问题时的第二层循环是一样的。
但是关键就是我们还需要两个值来保存这一段寻找空间(从i到n-1)的首尾也就是 i 和n的值,我们这里使用first和last表示首 和尾。那么根据二分法第一次查找到的最小值是(first+last)/2 那么我们肯定就需要判断我们找到的最小值和预先确定的最大值是否满足条件,如果满足了,那么说我们想找的最长的序列对应的最小值肯定还需要向后找,也就是first=m+1。如果不满足就是说我们找的最长的序列对应的最小值肯定在m的前面,就是last=n+1
#include <iostream>
using namespace std;
#include <set>
#include <algorithm>
#include <vector>
bool cmp(int a,int b)
{
return a>b;
}
int main()
{
long long n,p;
cin>>n>>p;
//vector<long long> sn(n);
long long sn[n];
for(int i=0; i<n; i++)
{
cin>>sn[i];
}
sort(sn,sn+n,cmp);
int len=0;
for(int i=0;i<n;i++){
int first=i;
int last=n-1;
/*
下面是关键
*/
while(first<=last){
int m=(first+last)/2;
if(sn[i]<=sn[m]*p){
if(m-i+1>len)
len=m-i+1;
first=m+1;
}
else{
last=m-1;
}
}
}
cout<<len;
}
然后还有个无法AC的思路
第一层的循环是确定最大值 和上面的方法一样,第二层循环就变换了一下,如果第二层循环从后向前,最开始循环得到的数字可能并不满足 最大值<=最小值*p ,满足了就break,这个肯定就是 这个最大值对应的最长的序列了。
但这样我试了 也还是会超时,真实原理还是没太搞懂,只是猜测是满足条件的序列其实不算多 也就是说不好找,如果按照第这样的思路 每个最大值都找到一个满足的序列,时间爆炸是必然的。第一个方法刚好相反
#include <iostream>
using namespace std;
#include <set>
#include <algorithm>
#include <vector>
bool cmp(int a,int b)
{
return a>b;
}
int main()
{
long long n,p;
cin>>n>>p;
//vector<long long> sn(n);
long long sn[n];
for(int i=0; i<n; i++)
{
cin>>sn[i];
}
sort(sn,sn+n,cmp);
int len=1;
for(int i=0;i<n;i++){
long long M=sn[i];
for(int pos=n-1;pos>=i+len-1;pos--){
if(M<=sn[pos]*p){
len=pos-i+1;
break;
}
}
}
cout<<len;
}