尺取法学习笔记

返回的推进区间开头和结尾,求满足条件的最小区间的方法称为尺取法。尺取法,顾名思义,像尺子一样,一块一块的截取。用尺取法来优化,使复杂度降为了O(n)。

整个过程分为四步:

          1.初始化左右端点

    2.不断扩大右端点,直到满足条件

    3.如果第二步中无法满足条件,则终止,否则更新结果

    4.将左端点扩大1,然后回到第二步

例题:Poj 3061   Poj 2566



下面贴代码(本人蒟蒻勿喷QwQ


<span style="color:#000000;">// POJ 3061

#include
#include
using namespace std;
const int maxn=100010;
int num[maxn];
int n,S;

int main()
{
	int t;scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&S);
		for(int i = 1; i <= n; i ++)
		{
		scanf("%d",&num[i]);
		}
		int sum = 0,s = 1,e = 1;
		int ans = n + 1;
		for(;;)
		{
			while(e <= n && sum < S)
			{
				sum += num[e++];
			}
			if(sum < S)
			{
				break;
			}
			ans = min(ans ,e - s);
			sum -= num[s++];
		}
		if(ans == n + 1)
		cout<<0<

//POJ 2566

#include 
#include 
#include 
using namespace std;

const int maxn = 100000 + 5;
const int inf = 0x3f3f3f3f;

int A[maxn],N,K;

struct Sum
{
    int s,p;
    inline bool operator < (const Sum &b)   const   { return s < b.s; }
} S[maxn];

inline void Qry(int x)
{
    int l = 0,r = 1,mn = inf;
    int rc,rc_l,rc_r;
    while(l <= N && r <= N)
    {
        int t = S[r].s - S[l].s;
        if(abs(t - x) < mn)
        {
            rc_l = S[l].p,rc_r = S[r].p;
            rc = t;     mn = abs(t - x);
        }
        if(t > x)       ++ l;
        else if(t < x)  ++ r;
        else            break;
        if(l == r)      ++ r;
    }
    if(rc_l > rc_r) swap(rc_l,rc_r);
    printf("%d %d %d\n",rc,rc_l + 1,rc_r);
}

int main()
{
    while(scanf("%d%d",&N,&K), N || K)
    {
        S[0].s = 0; S[0].p = 0;
        for(int i = 1;i <= N;i ++)  scanf("%d",A + i),S[i].s = S[i - 1].s + A[i],S[i].p = i;
        sort(S,S + 1 + N);
        while(K --)
        {
            int T;  scanf("%d",&T);
            Qry(T);
        }
    }
    return 0;
}

首先 第一题保证每个数不是负数,很简单的用尺取法秒过,但要注意无解输出0的情况。

其次第二题,不能保证每个数是否大于0,不能满足单调;

但是,我们要求的是数列和的绝对值,就可以做点什么事了。

绝对值满足性质:| sum[i] - sum[j] | == | sum[j] - sum[i] |

因此,我们就能改变前缀和的顺序了;

然后,就可以按照3061的尺取法思路求解了。


嗯嗯,就是这样。

尺取法大致就是这样,(雾



你可能感兴趣的:(小姿势)