Hello!大家好,我是@学霸小羊,今天来讲讲尺取法。
话说回来,枚举算法应该对大家来说很简单(不会还有新手不会吧)。
枚举又有许多高级的分支,都用于节省时间复杂度。
例如:二分查找和二分答案——
二分查找与二分答案_二分查找和二分答案的区别-CSDN博客文章浏览阅读102次。以在一个升序数组中查找一个数为例。它每次考察数组当前部分的中间元素,如果中间元素刚好是要找的,就结束搜索过程;如果中间元素小于所查找的值,那么左侧的只会更小,不会有所查找的元素,只需到右侧查找;如果中间元素大于所查找的值同理,只需到左侧查找。二分答案:答案有一个区间,在这个区间中二分,直到找到最优答案。二分查找:在一个已知的有序数据集上进行二分地查找。二分算法可以分为二分查找和二分答案。首先明确二分查找与二分答案有何区别?_二分查找和二分答案的区别https://blog.csdn.net/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【关于二分】-CSDN博客文章浏览阅读894次,点赞30次,收藏14次。二分的介绍https://blog.csdn.net/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,然后回到第二步
因为 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,这题就有点不一样了。
大家先想想吧!下一个博客揭晓答案!
标签:二分,松果,2522%,blog,算法,查找,取法,蹦蹦 From: https://blog.csdn.net/yangyanbin_sam/article/details/144583445