尺取法一般用来求解最大/最小区间问题。用两个指针代表区间的首尾,反复地推进区间的开头和末尾,来求取满足条件的最小/大区间,复杂度为O(n)。
当一个区间满足条件时,左指针推进,判断新区建是否满足条件,如果不满足条件,那么区间末尾向后扩展,直到满足条件为止。这样在区间推移过程中我们就得到了很多满足条件的区间,根据题意取最优即可。
当满足以下条件时,可以使用尺取法:若从开始,至少要到
才能满足条件,即:最初满足条件的区间为
,则从
开始的最初满足条件的区间为
,必然有t≤t'。
设若从开始总和最初不小于s时的连续子序列为
,这时
,所以从
开始总和最初大于s的连续子序列如果是
的话,则必然有t≤t'。所以可以用尺取法。
(1)以s = 0,t = 0,sum = 0初始化;
(2)只要依然有sum < S,就不断将sum累加,并将t加1;
(3)如果(2)中无法满足sum >= S则终止,否则更新res = min(res, t - s + 1);
(4)将sum减去,s增加1然后回到(2)。
由于t最多变化t次,故算法复杂度为O(n)。
代码如下:
#include <cstdio>
using namespace std;
#define N 100005
#define min(a, b) (a) < (b) ? (a) : (b)
int a[N], sum[N];
int main(){
int tc, n, s;
scanf("%d", &tc);
while(tc --){
scanf("%d %d", &n, &s);
sum[0] = 0;
for(int i = 1; i <= n; ++i)
scanf("%d", &a[i]), sum[i] = sum[i - 1] + a[i];
int beg = 1, en = 1, ans = N;
while(en <= n){
int tmp = sum[en] - sum[beg - 1];
if(tmp >= s)
ans = min(ans, en - beg + 1), ++beg;
else
++en;
}
printf("%d\n", ans == N ? 0 : ans);
}
return 0;
}