何为尺取
尺取法,便是形如用一把尺子的方法,一步一步去截取目标序列,最终满足一定的限制。
我个人理解更偏向于,尺取算法就像毛毛虫一样,有伸缩也有前进,在目标序列的一小节上进行操作(对左右节点操作等),使运行时间比单纯暴力少的多。
举个栗子
讲理论可能不太清楚,下面是一个尺取法的题目。
描述
冬日里的一抹暖阳总是能给人们留下深刻的记忆,人们喜爱冬天的太阳,就跟人们喜爱冬天的火锅一般。寒冷的冬天总会让人想起火锅,最近小Z特别想去吃火锅,刚好某家转转火锅刚开业有活动,有n盘火锅围成一个圈,第一盘和最后一盘是相连的,每一盘火锅都有一个价值a[i],现在可以吃连续的m盘火锅,小Z想知道他所吃的那连续的m盘火锅的最大价值可以是多少?你能帮帮憨憨的小Z吗。
格式
输入格式
第一行数入两个整数n,m(1<=m<=n<=2000000),分别表示火锅的盘数和可以吃的连续的盘数,第二行输入n的数ai,分别表示每一盘火锅的价值。
输出格式
输出一个整数,表示连续m盘火锅的最大价值。
样例
样例输入
5 3
6 1 2 5 3
样例输出
14
这个题目用暴力法可以每m个累加求和,求出最大,但是这需要运算多次,同时这个题n的数据比较大,暴力肯定不行。
用尺取解决这个问题的思路是:
我们已知尺子长度为m,我们便求出前m个火锅的价值和为sum,设左端点为0,右端点为m,不断移动尺子,同时比较交换与sum的大小,即可求解。
Tip
此题因为是个环,可以用一个while控制右端点小于n的,一个控制右端点超过n的。
下面为代码
#include <stdio.h>
int a[2000010];
int main()
{
int n,m;
while(~scanf("%d %d",&n,&m))
{
int i;
for(i=0; i<n; i++)
scanf("%d",&a[i]);
long long int sum=0;//此处用longlongint,和范围会超过int
for(i=0; i<m; i++)
sum+=a[i];
long long int temp=sum;
int l=0,r=m;
while(r<n)
{
temp-=a[l];
l++;
temp+=a[r];
r++;
if(temp>sum)
sum=temp;
}
r=0;
while(l<n-1)
{
temp-=a[l];
l++;
temp+=a[r];
r++;
if(temp>sum)
sum=temp;
}
printf("%lld\n",sum);
}
}
总结:尺取法我认为是一种对尺子进行操作的方法,你求得第一个尺子后(既找到第一个满足条件的区间),在根据题目的限制条件,求得解答的方法,时间明显要比暴力求法短的多。
注意事项
必须清楚后续确定所要求解的区间如何进一步得到根据其端点得到,否则无法使用尺分法,一定需要注意问题的分析。