1. 程式人生 > 實用技巧 >常用技巧:尺取法

常用技巧:尺取法

反覆推進區間的開頭和末尾,來求取滿足條件的最小區間的方法為尺取法。尺取法的名字來源於尺取蟲,來回推進開頭和末尾,逐步判斷當前的區間:若區間不滿足條件,則向前推進擴大區間;若區間滿足條件,記錄當前的解並推進末尾縮小區間。每個尺取的過程複雜度為O(n)。例題:POJ3061

需要注意的是,在模板中,當前開頭末尾分別為r、l時,區間其實是[l,r)

#include<stdio.h>
#include<algorithm>
using namespace std;
int n,s,ans,num[100005];
int main(){
    int t;
    scanf("%d
",&t); while(t--){ scanf("%d%d",&n,&s); ans=0x3f3f3f3f; for(int i=1;i<=n;i++) scanf("%d",&num[i]); int l=1,r=1,sum=0; while(true){ while(sum<s&&r<=n){ sum+=num[r++]; } if(sum<s) break
; ans=min(ans,r-l); sum-=num[l++]; } if(ans>n) printf("0\n"); else printf("%d\n",ans); } }

例題POJ3320,這道題如果不用尺取法就會超時

#include<stdio.h>
#include<set>
#include<map>
#include<algorithm>
using namespace std;
int n,num[1000005];
set
<int> s; map<int,int> m; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&num[i]); s.insert(num[i]); } int l=1,r=1,sum=0,cnt=s.size(),ans=0x7fffffff; while(true){ while(r<=n&&sum<cnt){ if(m[num[r++]]++ ==0){ sum++; } } if(sum<cnt) break; ans=min(ans,r-l); if(m[num[l++]]-- ==1){ sum--; } } printf("%d\n",ans); }