尺取法笔记
概述
尺取法可以在线性时间内解决这样的典型问题:给出一个长度为\(n\)的序列和一个整数\(k\),求区间和大于等于\(k\)的连续区间的最小长度。
对应地也可以求区间和小于等于\(k\)的连续区间的最大长度。
区间和可以换为定义在区间上的任意函数,比如区间中互不相同的数字的数量,或者区间最大值于最小值之差。如果区间函数值可以\(O(1)\)得到,那么尺取法的时间复杂度就是\(O(n)\)。
求区间和大于等于\(k\)的连续区间的最小长度
左端点\(l\)依次向右推进,对于每个\(l\),向右推进右端点\(r\)直到区间和刚好大于等于\(k\)满足条件,规定用左闭右开的方式表示区间,那么此时区间长度\(r-l\)就是左端点l对应的答案。随着左端点不断向右推进,会出现右端点到达最右端仍不能满足条件的情况,此时再推进左端点也不会得到合法区间,故跳出循环。
int ans = INF;
for (int l = 1, r = 1, sum = 0; l <= n; l++)
{
while (r <= n && sum < k) sum += a[r++]; // 如果不满足条件就不断推进右端点
if (sum <= k) break; // 如果到达最右端仍得不到合法区间,就跳出
ans = min(ans, r - l); // 找到合法区间,更新答案
sum -= a[l]; // 左端向右推进一步
}
求区间和小于等于\(k\)的连续区间的最大长度
左端点\(l\)依次向右推进,对于每个\(l\),向右推进右端点\(r\)直到区间和刚好大于\(k\)不满足条件的位置,此时区间长度减一\(r-l-1\)即为当前左端点\(l\)对应的答案。随着左端点不断向右推进,会出现右端点到达最右端仍然满足条件的情况,此时的区间长度\(r-l\)就是当前答案,因为之后再推进左端点得到的区间不会更长,故更新答案后跳出循环。
int ans = 0;
for (int l = 1, r = 1, sum = 0; l <= n; l++)
{
while (r <= n && sum <= k) sum += a[r++]; // 如果仍然满足条件就不断推进右端点
if (sum <= k) { ans = max(ans, r - l); break; } // 如果到达最右端仍然满足条件,就更新并跳出
ans = min(ans, r - l - 1); // 此时刚好不满足题意,区间长度减一为此时对应的答案
sum -= a[l]; // 左端点向右推进一步
}

浙公网安备 33010602011771号