题意:在给定的n个数中选出m个数(数的大小不超过10^9),记这m个数中的最大值为Max,最小值为Min,要求满足Max<=p*Min(p<=10^9),问所有满足条件的方案中m最大是多少。问题相当于:在一个给定的递增序列中,确定一个左端点a[i]和右端点a[j],使得a[j]<=a[i]*p成立,求j-i的最大值。
思路:
做法1:
首先需要立刻反应的是,p,Min均存储为int型整数,p*Min的值可能会溢出,因此应该(long long)p*Min,或者把输入数据全都定义成long long ing型也可以;然后,对于给定的序列,先从小到大排序。如下,定义指针low=0,high=n-1,maxidx=high(指向子序列的最大值)
1 2 3 4 5 6 7 8 9 20
low high/maxidx
第1次查找时(即low==0时),进入语句1,当退出while循环时,maxidx恰指向符合要求的最大值处,如下,
1 2 3 4 5 6 7 8 9 20
low maxidx high
第2,3...次查找时,根据条件Max<=p*Min,随着Min的增大,显然满足条件的最大值Max肯定在第一次指向的位置maxidx之后了,因此,从maxidx开始向右遍历,即进入语句2。
代码:
#include <cstdio> #include <algorithm> using namespace std; const int N=100005; int seq[N]; int main() { int n,p; scanf("%d%d",&n,&p); for(int i=0;i<n;i++) scanf("%d",&seq[i]); sort(seq,seq+n); int low=0,high=n-1; int maxSize=1; int maxidx=high; while(low<high){ if(low==0){ while(seq[maxidx]>(long long)seq[low]*p)//语句1 maxidx--; }else{ while(seq[maxidx]<=(long long)seq[low]*p && maxidx<=high)//语句2 maxidx++; maxidx--; } if(maxidx-low+1>maxSize) maxSize=maxidx-low+1; low++; } printf("%d",maxSize); return 0; }
做法2:使用upper_bound()函数。简单说明一下这个函数,该函数在<algorithm>下,函数接口为 ForwardIt upper_bound( ForwardIt first, ForwardIt last, const T& value ); 函数返回区间 [first,last) 内指向首个大于value的元素的迭代器,若不存在这样的元素,则返回尾迭代器last。另外注意要求该区间内的元素有序。如下,
vector<int> vec={1,2,3,4,5,6}; vector<int>::iterator it=upper_bound(vec.begin(),vec.end(),5);//C++11之后可用auto关键字 if(it!=vec.end())
printf("I find it,%d\n",*it);//返回6 else
printf("Such element does't exist\n"); it=upper_bound(vec.begin(),vec.end(),10); if(it!=vec.end())
printf("I find it,%d\n",*it); else
printf("Such element does't exist\n");//不存在大于10的数
因此,本题的做法就是,顺序遍历有序数组,对于每一个seq[i],寻找首个seq[j],使其seq[j]>seq[i]*p,然后记录并比较区间的长度j-i即可,代码如下:
#include <cstdio> #include <vector> #include <algorithm> using namespace std; int main() { int n,p; scanf("%d%d",&n,&p); vector<int> seq(n); for(int i=0;i<n;i++) scanf("%d",&seq[i]); sort(seq.begin(),seq.end()); int maxSize=1; for(auto it=seq.begin();it!=seq.end();it++){ auto maxIter=upper_bound(it+1,seq.end(),(long long)*it*p);//第一个*是解引用,第二个*是乘号 if(maxIter-it>maxSize) maxSize=maxIter-it; } printf("%d\n",maxSize); return 0; }