尺取法
尺取法通常是指对数组保存一对下表(起点,终点),然后根据实际情况交替推进两个端点直接得出答案的方法,这种操作很像是尺蠖(日文中称为尺取虫)爬行的方式故得名。
(以上文字摘自挑战程序设计竞赛第2版)
Codeforces中显示它的算法名称叫做"two pointers". 直译成中文的话叫双指针法. 怎么说呢……做到提高组之后,很多oier仅仅是觉得好像有这么一个两个指针从左到右扫一遍的算法存在,却不知道它的名字.(其实是因为大佬们根本没把它当个算法) 这个算法不是很难,却很有意思. 尺取法是一种比较基础的算法,一般用来解决具有单调性的区间问题. 当然,说到单调性,大家都会想到二分. 尺取法能做的题,有很大的概率也可以用二分解决,不过尺取和二分的复杂度在不同的题目中往往是不同的. 所以尺取法的题大概也是找到可二分性然后优化之. 我想不到类比尺取法的实际问题,所以我只能给了很多例题. 大家如果有的话可以告诉我,我将非常感谢. 维护两个指针 l,r ,每次确定区间的左端点,让 r 不断向右移动,直到满足条件停下,维护一下答案,直到 r>n 或者其它情况(视题目而定). 而实际中很多算法都要用到尺取法来辅助或者优化计算,尤其是分治算法.
(摘自洛谷日报第73期,原文章链接:https://baijiahao.baidu.com/s?id=1615129382322508344&wfr=spider&for=pc,原文作者:Fuko_Ibuki)
这个题目先对数组进行排序,这个问题就成为了具有单调性的区间问题,定义两个指针head,tail,tail取便所有数组,判断a[head]<=p*a[tail](不难发现,此时a[head]是完美数列中的最小值,a[tail]是最大值),符合条件就更新最大长度,如果不符合就让头指针++
参考sxy大佬的代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
long long a[100001];
int main()
{
int n,p,i;
cin>>n>>p;
for(i=0;i<n;i++) cin>>a[i];
sort(a,a+n);
int head=0,tail=0,ans=1;
while(tail<n-1)
{
while(a[tail]<=p*a[head]&&tail<=n-1)
{
ans=max(ans,tail-head+1);
++tail;
}
while(a[tail]>p*a[head])
++head;
}
cout<<ans;
return 0;
}