常用技巧:尺取法

反复推进区间的开头和末尾,来求取满足条件的最小区间的方法为尺取法。尺取法的名字来源于尺取虫,来回推进开头和末尾,逐步判断当前的区间:若区间不满足条件,则向前推进扩大区间;若区间满足条件,记录当前的解并推进末尾缩小区间。每个尺取的过程复杂度为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);
}

 

posted @ 2020-10-09 15:20  太山多桢  阅读(202)  评论(0)    收藏  举报