题目大意
给定一个长度为 的正整数序列和一个整数
,求最短的连续子序列,使其元素和大于等于
。输出最短长度,若不存在则输出
。
解题思路
本题是经典的尺取法(双指针) 应用。由于序列中所有元素均为正整数,满足单调性,可以使用双指针高效求解:
- 初始化:左右指针
left = 0和right = 0,当前子序列和sum = 0,最小长度min_len = n + 1(初始设为不可能的大值)。 - 移动右指针:不断将
a[right]加入sum,直到sum >= S。 - 移动左指针:当
sum >= S时,尝试向右移动左指针:- 更新最小长度:
min_len = min(min_len, right - left + 1) - 从
sum中减去a[left],左指针右移 - 重复直到
sum < S
- 更新最小长度:
- 终止条件:右指针遍历完整个序列后,若
min_len未被更新(仍为n+1),说明无解,输出;否则输出
min_len。
时间复杂度:,每个元素最多被左右指针各访问一次。
空间复杂度:,存储序列。
C++ 代码实现
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--) {
int n, S;
cin >> n >> S;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
int left = 0;
long long sum = 0; // 使用 long long 防止大数溢出
int min_len = n + 1; // 初始化为不可能的大值
for (int right = 0; right < n; right++) {
sum += a[right]; // 扩展右边界
// 当前和满足条件时,尝试收缩左边界
while (sum >= S) {
min_len = min(min_len, right - left + 1);
sum -= a[left];
left++;
}
}
if (min_len == n + 1) {
cout << 0 << '\n';
} else {
cout << min_len << '\n';
}
}
return 0;
}
双指针循环:
- 右指针
right从到
遍历序列,将元素加入
sum。 - 左指针
left在sum >= S时向右移动,更新最小长度并减少sum。
关键点
- 正整数序列的单调性:保证了当左指针右移时,子序列和严格递减,不会跳过最优解。
- 边界更新时机:在收缩左指针前更新
min_len,确保记录的是当前满足条件的最短子序列。 - 无解判断:通过初始化
min_len = n + 1,最后检查该值是否被更新。

京公网安备 11010502036488号