「学习笔记」尺取法

尺取法,是一种 简单而有效的小算法

在外国网站上,它的名字叫做 two pointers,直译过来就是 双指针

这个外国名直接概括了尺取法的特点:

定义两个变量 \(l\) 和 \(r\)(不一定 是指针),

一开始先对 \(l\) 进行 移动操作

直到 \(l\) 确定 为止。

之后,我们开始移动 \(r\),

等到 \(r\) 确定时,

就可以 记录答案

例题:

1. POJ3061 Subsequence

温馨提示:题面是英语

给定长度为 \(N\) 的数组 \(A\) 和一个整数 \(M\),求总和不小于 \(M\) 的 子串最小长度

举个栗子:
\[n=10,m=15\]

\[A=\{5,1,3,5,10,7,4,9,2,8\}\]

那么我们先定义一个变量 \(sum\),存 当前子串的和,从左边第一个数开始,直到 \(sum\ge M\) 为止,再记录 当前长度

其实,

这个思想相当于 不满足条件就入队

然后得到队列长度,

再将队首元素 出队

再进行下一次的入队,

直到满足条件再次出队,

并且将这一次的长度与历史最短长度进行 取舍

遍历到最后的元素 无法再满足入队条件 的时候就结束。

此时用 \(O(n)\) 级别的时间复杂度就可以得到答案。

Code:

#include<bits/stdc++.h>
#define min(a,b) ((a)<(b)?(a):(b))
#define tense(a,b) (a=min(a,b))
using namespace std;
int t,n,m,a[100005];
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        int ans=n+1,now=0,flag=0; //答案设为最大值(n+1)
        for(int i=1;i<=n;i++)
            scanf("%d",a+i);
        queue<int>q;
        for(int i=1;i<=n;i++)
        {
            q.push(a[i]);
            now+=a[i];
            if(now>m)
            {
                while(now>=m) //记录答案
                {
                    flag=1;
                    int tmp=q.size();
                    tense(ans,tmp);
                    now-=q.front();
                    q.pop();
                }
            }
        }
        if(!flag)
            puts("0");
        else
            printf("%d\n",ans);
    }
    return 0;
}
上一篇:洛谷 P2357 守墓人


下一篇:复健训练难题扫除计划