今天老师讲了提了下尺取,于是就有了这篇笔记
PS:我觉得我们老师形容得很贴切,尺取就像虫子蠕动一样
例题:
给出一个序列,求区间和大于或者等于S的最短区间长度.
我们假设序列长度为10,S为15,序列为
3 2 8 10 5 3 8 4 2 9
尺取法的思路大概就是,我们使用三个变量L,R,tot, 表示区间的左右两边和区间权值和
先初始L=R=1,tot=0
然后开始蠕动
过程:
i: 1 2 3 4 5 6 7 8 9 10
ai: 3 2 8 10 5 3 8 4 2 9
L: +
R: +
计算区间tot+=a[R] 再将R向前移动一位
得出tot=3,比S小,所以我们将R向后移动
i: 1 2 3 4 5 6 7 8 9 10
ai: 3 2 8 10 5 3 8 4 2 9
L: +
R: +
计算区间tot+=a[R] 再将R向前移动一位
得出tot=5,比S小,继续移动R
i: 1 2 3 4 5 6 7 8 9 10
ai: 3 2 8 10 5 3 8 4 2 9
L: +
R: +
计算区间tot+=a[R] 再将R向前移动一位
得出tot=13,比S小,继续移动R
i: 1 2 3 4 5 6 7 8 9 10
ai: 3 2 8 10 5 3 8 4 2 9
L: +
R: +
计算区间tot+=a[R] 再将R向前移动一位
得出tot=13,比S小,继续移动R
i: 1 2 3 4 5 6 7 8 9 10
ai: 3 2 8 10 5 3 8 4 2 9
L: +
R: +
计算区间tot+=a[R] 再将R向前移动一位
得出tot=23,比S大,停止移动R,记录当前区间长度K=4
i: 1 2 3 4 5 6 7 8 9 10
ai: 3 2 8 10 5 3 8 4 2 9
L: +
R: +
因为R已经不能再移动,因此我们移动L
计算区间tot-=a[L] 再将L向前移动一位,因为原本L所在位置已经不属于这个区间了
得出tot=20,比S大,R继续不动,记录当前区间长度K=3
i: 1 2 3 4 5 6 7 8 9 10
ai: 3 2 8 10 5 3 8 4 2 9
L: +
R: +
计算区间tot-=a[L] 再将L向前移动一位
得出tot=18,比S大,R继续不动,记录当前区间长度K=2
i: 1 2 3 4 5 6 7 8 9 10
ai: 3 2 8 10 5 3 8 4 2 9
L: +
R: +
计算区间tot-=a[L] 再将L向前移动一位
得出tot=10,比S小,重新移动R
i: 1 2 3 4 5 6 7 8 9 10
ai: 3 2 8 10 5 3 8 4 2 9
L: +
R: +
计算区间tot+=a[R] 再将R向前移动一位
得出tot=15,等于S,R不动,移动L,记录区间长度K=2
以下过程省略...
观察这个过程,这就是尺取法了,如果把L看做虫的尾巴,R看做虫的头,那么完全就是一个蠕动的过程:
L_/\_R
先向右延伸区间
L_____R
到达边界后向左缩小区间
L_/\_R
L_____R
L_/\_R
L_____R
以下是毒瘤代码实现:
#include<bits/stdc++.h>
#define RE register
#define IOS ios::sync_with_stdio(false)
#define (x) [x]
#define cin(x) cin>>x
#define cout(x) cout<<x
#define Endl puts(" ")
#define Ww(i,j) while(i<=j)
#define WW(i1,j1,i2,j2) while(i1<j1&&i2<j2)
#define B break;
#define Length(l,r) r-l+1
#define MMIINN(a,b) a<=b?a:b
#define INF 2147483647
#define F(s,j) for(int i=s;i<=j;++i)
#define R return
#define I(i,j) if(i<j)
using namespace std;
int S,n,a[1000001],l=1,r=0,now=0,ans=INF;
int main (){
IOS;
cin(n),cin(S);
F(1,n)
cin(a(i));
Ww(l,n){
WW(now,S,r,n) now+=a(++r);
I(now,S) B;
ans=MMIINN(ans,Length(l,r));
now-=a(l++);
}
cout(ans);
Endl;
R 0;
}