尺取法的应用
最近准备笔试的过程中,发现好多算法题,有点套路的感觉,只怪自己平时积累不多,现在只能亡羊补牢了。
关于尺取法的概念我就不介绍了,网上这方面讲解很多,主要说一些应用的方面,积累下目前遇到的一些可以用尺取法可以来解决的题目,不定期更新下。
尺取法参考博客
在此说明,我的梦想是进入网易,校招进入网易已经不可能了,我就在这里立一个誓言,将来一定会进网易
尺取法的应用
简单可以理解为,所有求连续的一段区间的相关问题(比如求和,求差),都可以考虑下尺取法。注意,尺取法需要保证所有点按顺序排列好,只能勇往直前,而不能后退。
尺取法要维护
左右
两个点,左边这个点慢一些,右边这个点走的快一些,两个点间的尺
不能超过最大的临界
,否则就要移动点。
1:搜狗校招9月08号笔试题
题意:同一个圆上面有n个点,n个点按照从小到大排列,每个点有一个角度a(0<=a<=360),代表圆心角,问题是让你求这些点构成的所有劣弧对应最大的圆心角(按劣弧来算,比如,10,193,圆心角就为177)
思路:这里就以180为’临界’,左右两个点距离为’尺’,两个点都从原点出发,顺时针走,如果大于180,左点前进一位,如果小于180,右点向前进一位。因为圆是循环的,所以左边这个点必须走遍圆中所有点,才能访问完所有的最大的劣弧(注意,不需要访问所有劣弧,只需要访问所有最大的劣弧,这里的最大,是指对于某个点来说的最大)。右边的点在没走完一圈之前,采用a[right]-a[left]计算,右点走完一圈之后,采用360-a[left]+a[right%n]来计算。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
bool exp(double a,double b){
if(a-b>1e-12)
return true;
else
return false;
}
int main()
{
int n;
scanf("%d",&n);
double a[n+10];
for(int i=0;i<n;i++){
scanf("%lf",&a[i]);
}
double ans = -1;
int left=0,right=0;
while(left<n){
if(right<n){
double temp = a[right]-a[left];
if(exp(temp,180)){
left++;
}else{
ans = max(ans,temp);
right++;
}
}
else{
double temp = 360-a[left]+a[right%n];
if(exp(temp,180)){
left++;
}else{
ans = max(ans,temp);
right++;
}
}
}
printf("%.8lf\n",ans);
return 0;
}
/*
4
10.00000000
180.00000000
183.00000000
198.00000000
3
10.00000000
180.00000000
190.00000000
*/
小优化 :第一次寻找最大尺的时候,可以采用二分法。这个题必须是有序的,如果无序,需要先排序再操作,否则,不满足
变量递增
的效果。
2.最长子区间问题
题意:要求求出最长连续子区间,区间要求所有值加起来,是k的倍数。例如,区间1,2,3,4,5,给出k=5,那么满足要求的区间有[1,2,3,4],[2,3],[1,2,3,4,5],显然[1,2,3,4,5]最长
思路:这个题跟尺取法唯一相同点就是连续问题。需要记录前缀和。因为题目要求为最大,所以需要记录第一个出现该值的位置,再次出现该值时,说明从第一次出现到当前出现位置,所有数加起来为k的倍数。
memset(dp,-1,sizeof(dp));
int temp=0,ans=0;
for(int i=0;i<n;i++){
temp = (temp+a[i])%k;
if(dp[temp]!=-1){
len = i-dp[temp];
ans = max(ans,len);
}else{
dp[temp] = i;
}
}
printf("%d\n",ans);
3.滴滴出行9月10号 xor问题
题意:给你一段区间,求出最大的满足不重叠连续子区间的个数,子区间要求区间内所有数异或为0
思路:这里和上面一道题目很像,不同的是本题要求的操作为异或。搞清楚一点:从左往右扫,满足条件的第一个区间,中间肯定没有子区间满足条件,所以这个区间一定构成最大子区间个数中的其中一个(即使中间部分可能和后面的连续序列异或后满足条件,也不在做计算,比如[4,3,2,4,3,2,3,2],[432432]为子区间[3232]也可以为子区间,但是一定不会选择[3232],因为他所消耗的区间长度比[4,3,2,4,3,2]长,[3,2,3,2]相当于消耗了这一整个序列)。
这里稍微说一下相似的题目,毕竟找工作,多拓展一点没坏处。
题目:求最大不相交区间的个数。
这个题采用贪心的算法,要记录下所有区间的left点和right点,然后根据right点的大小来排序,排序后从左往右找,
后记:
还有一些尺取法的应用,都可以采用尺取法套路一下,后面再接着更新,如果有错误的地方,希望大家不吝赐教,毕竟本人小菜鸡