尺取法的小讲解

来源:互联网 发布:python图形化界面开发 编辑:程序博客网 时间:2024/04/30 06:33

例题 poj– 3061
//大概思路: 用两个指针不断去缩小范围, 知道我们所需要的最优答案出现. 一种思想, 需要多想想.
//比如说上面这道题: 题意就是求在给定的序列中长度最小的子串, 并且该串的和要不小于所给的S.
//思路大概就是尺取法: 附上一张图.
这里写图片描述
黄色部分就是尺取的部分, 可以发现我们一定经历过最优解.

AC Code

const int maxn=1e5+5;int n,s;int a[maxn];void solve(){    scanf("%d%d",&n,&s);    for(int i=0;i<n;i++) scanf("%d",&a[i]);    int l=0,r=0,res = inf,sum = 0;    while(1){        while(r<n && sum < s){  //一直向右尺取            sum += a[r];            r++;        }        if(sum<s) break;        res = min(res,r-l);    //更新答案.        sum -= a[l++];    }    if(res == inf) printf("0\n");    else printf("%d\n",res);}

//可以发现就是双指针移动的选择.

再来一道题 :
传送门 poj – 3320
//题意: 从给定的序列中选取一个最短的区间使得包含所有出现过的数.
//思路 : 当然还是尺取, 从每一点开始, 记录出现过的数的次数. 然后缩进就是了. 难点在于如何计数. 每一个数的范围是不超过int的范围. 我的第一想法就是离散化. 这样1e6我就可以进行标记. 我觉得这样应该是可以做的, 就是麻烦了点. 所以请教了大神后知道可以用map来做标记和计数啊, map可是long long 的数都可以进行标记的! 所以用map就很好做了.

AC Code

const int maxn=1e6+5;int n,s;int a[maxn];void solve(){    while(~scanf("%d",&n)){        map<int,int>mp;        int cnt = 0;        for(int i=0;i<n;i++){            scanf("%d",&a[i]);            if(!mp[a[i]]) cnt++;            mp[a[i]] = 1;        }        mp.clear();        int l = 0, r= 0,sum = 0, res = inf;        while(1){            while(r<n && sum<cnt){                if(!mp[a[r]]) sum++;                mp[a[r]]++;                r++;            }            if(sum<cnt) break;            res = min(res,r-l);            mp[a[l]]--;    //不断缩进.            if(!mp[a[l]]) sum--;            l++;        }        printf("%d\n",res);    }}
原创粉丝点击