#5495. 尺取法笔记
尺取法笔记
尺取法课堂笔记(这个笔记是针对最短子序列这道题目)
一、算法核心思想
尺取法本质是双指针技术,通过左右两个指针(l为左指针,r为右指针)框选一段连续区间,根据区间是否满足条件动态移动指针,高效求解“连续区间相关问题”(如本题的“和≥m的最短区间长度”)。
核心逻辑分两种情况:
- 当当前区间(
[l, r])满足条件时:记录最优解(如区间长度),并向右移动左指针(l++),尝试缩小区间找更优解。 - 当当前区间(
[l, r])不满足条件时:向右移动右指针(r++),扩大区间范围,直至区间满足条件。
二、代码逐段解析
1. 头文件与宏定义
#include<bits/stdc++.h> // 万能头文件,包含常用STL库
#define int long long // 将int类型重定义为long long,避免数据溢出
const int N = 1e6 + 10; // 定义数组最大容量(1e6+10),适配题目数据规模
using namespace std; // 启用std命名空间,简化代码(无需重复写std::)
2. 变量声明
int n, m, a[N], s[N], ans = 1e9;
// n:数组元素个数;m:目标区间和的最小值
// a[N]:存储原始数组;s[N]:存储前缀和数组(用于快速计算区间和)
// ans:初始化答案为极大值(1e9),后续更新最小值
3. 主函数流程
(1)输入数据与前缀和初始化
signed main() { // 因#define int long long,主函数用signed避免编译警告
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i]; // 输入第i个元素
s[i] = s[i - 1] + a[i]; // 前缀和公式:s[i] = 前i个元素的和
}
(2)双指针循环(核心逻辑)
int l = 1, r = 1; // 初始化左右指针,均从第一个元素开始
while (l <= n && r <= n) { // 指针不越界时循环
int sum = s[r] - s[l - 1]; // 用前缀和快速算区间和:[l, r]的和
if (sum >= m) { // 情况1:区间和满足条件(≥m)
ans = min(r - l + 1, ans); // 更新最短区间长度(当前长度vs已有答案)
l++; // 左指针右移,尝试缩小区间
} else { // 情况2:区间和不满足条件(<m)
r++; // 右指针右移,尝试扩大区间
}
}
(3)输出结果
if (ans == 1e9) cout << 0; // 若ans未更新(无满足条件的区间),输出0
else cout << ans; // 否则输出最短区间长度
return 0;
}
三、算法关键总结
- 时间复杂度:O(n)。每个元素仅被左右指针各遍历1次,无嵌套循环。
- 适用场景:求解“连续区间的最值问题”(如最短/最长区间、和满足条件的区间等),且数组元素非负(保证指针移动后区间和单调变化,无需回退)。
- 易错点:
- 前缀和数组从
i=1开始初始化,避免i=0时的边界计算错误。 - 答案初始值需设为极大值(如1e9),确保能被有效更新;最终需判断是否存在有效区间(避免输出初始极大值)。
- 前缀和数组从
四、示例辅助理解
假设输入:n=5, m=11,a=[1,2,3,4,5]
- 前缀和
s=[0,1,3,6,10,15] - 指针移动过程:
l=1, r=1:sum=1 <11 → r=2l=1, r=2:sum=3 <11 → r=3l=1, r=4:sum=10 <11 → r=5l=1, r=5:sum=15 ≥11 → ans=5,l=2l=2, r=5:sum=14 ≥11 → ans=4,l=3l=3, r=5:sum=12 ≥11 → ans=3,l=4l=4, r=5:sum=9 <11 → r=6(越界),循环结束
- 最终输出
ans=3(对应区间[3,5],和为12)