【算法】尺取法

Hello!大家好,我是@学霸小羊,今天来讲讲尺取法。

话说回来,枚举算法应该对大家来说很简单(不会还有新手不会吧)。

枚举又有许多高级的分支,都用于节省时间复杂度。

例如:二分查找和二分答案——

二分查找与二分答案_二分查找和二分答案的区别-优快云博客文章浏览阅读102次。以在一个升序数组中查找一个数为例。它每次考察数组当前部分的中间元素,如果中间元素刚好是要找的,就结束搜索过程;如果中间元素小于所查找的值,那么左侧的只会更小,不会有所查找的元素,只需到右侧查找;如果中间元素大于所查找的值同理,只需到左侧查找。二分答案:答案有一个区间,在这个区间中二分,直到找到最优答案。二分查找:在一个已知的有序数据集上进行二分地查找。二分算法可以分为二分查找和二分答案。首先明确二分查找与二分答案有何区别?_二分查找和二分答案的区别https://blog.youkuaiyun.com/yangyanbin_sam/article/details/138767413?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522f22f19e4b28d48cc5a37b5931aa9cfdb%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=f22f19e4b28d48cc5a37b5931aa9cfdb&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-138767413-null-null.nonecase&utm_term=%E4%BA%8C%E5%88%86&spm=1018.2226.3001.4450【关于二分】-优快云博客文章浏览阅读894次,点赞30次,收藏14次。二分的介绍https://blog.youkuaiyun.com/yangyanbin_sam/article/details/138797821?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522f22f19e4b28d48cc5a37b5931aa9cfdb%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=f22f19e4b28d48cc5a37b5931aa9cfdb&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-2-138797821-null-null.nonecase&utm_term=%E4%BA%8C%E5%88%86&spm=1018.2226.3001.4450但进行二分是有前提的:数据要有序。

那么如果要在一些数中取一段,但又必须保持原来的顺序……

请看题目(又不是VCR)!

题目描述

给定一个长度为 N ( 10 < N < 100000 )的正整数序列。每个正整数都小于等于 10000 ,给定一个正整数 S ( S < 100000000 )。编写一个程序找到一个最小长度的子序列,要求这个子序列的和大于等于 S 。如果解不存在,则输出 0 。

输入格式

第一行两个整数 N 和 S;

第二行包括 N 个正整数表示数列 A ,两两之间用空格分隔。

输出格式

一个整数。

样例

输入数据 1

5 11
1 2 3 4 5

输出数据 1

3

这题就要用到尺取法了。

在做题前,先了解一下理论知识。

尺取法是一种线性算法,也是一种高效的枚举区间的方法。

记 (l,r) 两个端点为一个序列内以 l 为起点的最短合法区间,如果 r 随 l 的增大而增大的话,我们就可以使用尺取法。

具体的做法是:

  1. 初始化左右端点

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

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

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

因为 r 随 l 增大而增大,所以 r 只有 n 次变化的机会,所以时间复杂度为 O(n) 。

经典例子

给定一个长度为 n 的数列 a_1, a_2, ..., a_na1​,a2​,...,an​ 及整数 S ,求不小于 S 的连续子序列的和的最小值,假设解肯定存在。

input

n=10,S=15

a= {5,1,3,5,10,7,4,9,2,8}

尺取法示意图

首先,取得满足条件的最小序列,然后依次将响右移动,若 i 向左移动的过程中序列仍然满足条件,则不移动,否则将也向右移动。

在移动的过程中记录 j-i 的最小值。

好啦,刚才已经讲了基本思路,打一打刚才题目的代码吧!

参考代码 

#include<bits/stdc++.h>
using namespace std;
long long n,a[100005],s[100005],mi=INT_MAX,sum,i=1,j;
long long ss;
int main()
{
    cin>>n>>ss;
    for(int x=1;x<=n;x++)
    {
    	cin>>a[x];
        sum+=a[x];
        s[x]=s[x-1]+a[x];
        if(sum>=ss) sum=INT_MIN,j=x;
    }
    for(;j<=n;)
    {
        if(s[j]-s[i-1]>=ss) mi=min(j-i+1,mi),i++;
        else j++;
        if(i==j) j++;
    }
    if(mi==INT_MAX)mi=0;
    cout<<mi;
    return 0;
}

接下来,讲一个变式。

题目描述

大森林有熊兄弟的好朋友松鼠蹦蹦,一天蹦蹦来到一条很长的小 路,发现沿路地上都有松果,高兴极了,决定尽可能多吃松果。

蹦蹦观察到,每个松果的重量并不一定相同,可蹦蹦的肚子容量 有限,总共最多只能吃重量 C 的松果。
蹦蹦吃东西有个特点,一旦开吃就会不停的吃,不会漏过路上碰 到松果,直到遇到一个吃不下或吃完停止。也就是说松鼠蹦蹦只会吃 连续一段的松果。

已知路上共有 N 个松果,顺序的重量是 w_1, w_2,..., w_nw1​,w2​,...,wn​ 。蹦蹦最多可能吃多少颗松果?

输入格式

第一行,二个正整数,空格分开,表示 N 和 C,N 范围在[1..50000], C 范围在[1..1000000]。

第二行,N 个正整数,空格分开,表示从 w_1、w_2,..., w_nw1​、w2​,...,wn​ ,即松果的重量。每个松果重 量范围在[1..1000]。

数据范围

一个正整数,蹦蹦可以吃到的最多松果数量。

输出格式

样例

输入数据 1

5 5
3 1 2 1 1

输出数据 1

4

输入数据 2

9 5
1 5 4 3 2 1 1 4 1

输出数据 2

3

样例解释

样例 1 :吃:(1,2,1,1)这段的松果。

样例 2 :吃:(2,1,1)这段的松果。

原来我们是符合条件就更新minn或maxx,这题就有点不一样了。

大家先想想吧!下一个博客揭晓答案!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值