最美丽的区间
#include <bits/stdc++.h> // 双指针滑动窗口(双指针)
using namespace std;
typedef long long ll;int main()
{ll n, S, length;cin >> n >> S;vector<ll> a(n+1);for(ll i=1;i<=n;i++){cin >> a[i];}ll sum =0;ll minlength = n+1;ll left =1;for(ll right=1;right<=n;right++){sum +=a[right]; //扩展窗口// 当窗口内的和 >= S,收缩窗口while(sum>=S){minlength = min(minlength,right-left+1);sum -=a[left]; // 缩小窗口left ++;}}if(minlength == n+1)// 没有找到符合条件的区间{cout << 0 <<endl;}else {cout << minlength<<endl;}return 0;
}
前缀和+二分
#include <bits/stdc++.h> // 前缀和+二分法
using namespace std;
typedef long long ll;ll n, S;bool valid(ll mid,vector<ll>& b)
{for(ll j=1;j + mid - 1 <=n;j++){ll l = j;ll r = j + mid - 1;if(b[r]-b[l-1] >= S )return true;}return false;
}int main()
{ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);cin >> n >> S;vector<ll> a(n+1);vector<ll> b(n+1);ll max_num =-1; for(ll i=1;i<=n;i++){cin >> a[i];b[i]=b[i-1] + a[i];max_num= max(max_num,a[i]);}if(b[n] < S )cout << 0 << endl;else if (max_num >= S)cout << 1 << endl;else{ll l=1;ll r =n;while(l<r){ll mid = (l+r) >>1;if(valid(mid,b))r=mid;elsel=mid+1;}cout << r << endl;// cout << l << endl;}return 0;
}
while (l < r) {ll mid = (l + r) >> 1;if (valid(mid)) r = mid; // ✅ mid 可行,往更短尝试else l = mid + 1; // ❌ mid 不行,必须加长
}
第一个满足条件的最小值,也叫 lower_bound 类似逻辑。
-
valid(mid)
是一个单调性函数(子数组长度越大,越可能满足sum ≥ S
) -
所以:
-
若
mid
合法,说明答案可能更小 → 把r = mid
(保留 mid) -
若
mid
不合法,说明答案只能更大 → 把l = mid + 1
-
这个模板二分会不断逼近最小可行值,直到找到第一个合法值(最小长度)。
假设答案是 4
l = 1, r = 7
第一次 mid = 4 → valid(4) = true → r = 4
第二次 mid = 2 → valid(2) = false → l = 3
第三次 mid = 3 → valid(3) = false → l = 4此时 l == r == 4,结束循环
此时的 r
(或 l
)就是第一个满足条件的最小长度
可以输出
cout << r << endl; // ✅ 正确,r 是最小满足条件的长度
也可以输出
cout << l << endl; // ✅ 同样正确,因为最后 l == r
不建议改成 while (l <= r)
,否则逻辑就需要额外调整,不然会出错
while (l < r) {ll mid = (l + r) >> 1;if (valid(mid))r = mid;elsel = mid + 1;
}
左闭右开/左闭右闭的二分法,用于查找第一个满足条件的值(lower bound)。
-
每次都保留合法的 mid(即
r = mid
) -
最终
l == r
时退出,l
/r
就是我们要找的 最小满足条件的位置
如果你改成 while (l <= r)
,就会多循环一次:
while (l <= r) {ll mid = (l + r) >> 1;if (valid(mid))r = mid; // 问题点:当 mid == r 时,这样写 r 会一直不动elsel = mid + 1;
}
如果你不做特别处理:
-
可能陷入死循环(
l == r
,但 valid(mid) 一直为 true,r 永远不变) -
或者你最后拿到的
r
并不是“最小合法值”
注释
#include <bits/stdc++.h>
using namespace std;
typedef long long ll; // 简化 long long 为 ll,便于书写ll n, S; // 输入的数组长度 n 和目标子段和 S// 检查是否存在长度为 mid 的子数组,其和 >= S
bool valid(ll mid, vector<ll>& b)
{for (ll j = 1; j + mid - 1 <= n; j++) // 枚举所有长度为 mid 的连续子数组{ll l = j;ll r = j + mid - 1;// 前缀和优化:b[r] - b[l - 1] 即为 a[l] 到 a[r] 的和if (b[r] - b[l - 1] >= S)return true; // 如果有一个满足条件的,就返回 true}return false; // 所有区间都不满足,则返回 false
}int main()
{ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); // 提升 cin/cout 效率(关闭同步)cin >> n >> S;// 初始化数组 a 和前缀和数组 b,开 n+1 是因为我们从下标 1 开始vector<ll> a(n + 1);vector<ll> b(n + 1);ll max_num = -1; // 记录数组中的最大值for (ll i = 1; i <= n; i++){cin >> a[i]; // 读入原数组b[i] = b[i - 1] + a[i]; // 构建前缀和数组max_num = max(max_num, a[i]); // 更新最大值}// 边界情况 1:整个数组和都小于 S,不可能满足条件if (b[n] < S)cout << 0 << endl;// 边界情况 2:数组中有单个元素就 ≥ S,那最短子数组长度为 1else if (max_num >= S)cout << 1 << endl;// 否则,二分查找最短满足条件的子数组长度else{ll l = 1; // 最短可能长度ll r = n; // 最长可能长度(整个数组)// 二分查找最小满足条件的长度while (l < r){ll mid = (l + r) >> 1;if (valid(mid, b)) // 如果存在一个长度为 mid 的子数组满足条件r = mid; // 缩小范围,尝试更短的长度elsel = mid + 1; // 否则尝试更长的长度}cout << r << endl; // 最终的最短长度(即最左满足条件的值)}return 0;
}
滑动窗口法
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;int main()
{ll n, S, length;cin >> n >> S; // 输入数组长度 n 和目标和 Svector<ll> a(n + 1); // 定义数组,下标从 1 开始for (ll i = 1; i <= n; i++){cin >> a[i]; // 读入数组元素}ll sum = 0; // 当前窗口内的元素和ll minlength = n + 1; // 初始化最短长度为不可能的最大值ll left = 1; // 滑动窗口的左边界for (ll right = 1; right <= n; right++){sum += a[right]; // 扩展右边界(窗口右移)// 当窗口内的和满足条件时,尝试收缩左边界while (sum >= S){minlength = min(minlength, right - left + 1); // 更新最短长度sum -= a[left]; // 缩小窗口:移除最左边的元素left++; // 左指针右移}}// 输出结果:如果没有任何满足条件的区间,则输出 0if (minlength == n + 1){cout << 0 << endl;}else{cout << minlength << endl;}return 0;
}
1372