【尺取法】尺取法及运用

尺取法

(ps:参考书籍:挑战程序设计)

尺取法:尺取法通常是对数组保存一对下标,即所选取的区间的左右端点,然后根据实际情况不断地推进区间左右端点以得出答案。
是我们经常需要用到技巧。 (尺取法还是比较好懂的)

什么情况下能使用尺取法?
如果题目要求,给出一个数列,求其中一段连续的子序列的总和要大于/或小于等一个数值,然后要求出最小的长度。
换句话说,必须得是连续的一段子序列:就像它的名字,尺取,一定是要连续一段。然后从前往后行走。
还有就得看具体题目了。

相关运用

直接参考书中例题。
在这里插入图片描述
如图,尺取步骤:
1。开始第一步,我们直接从左到右 先累加到 大于等于 S,记录此时长度
2。然后减掉最左边的一个数值,如果还大于等于S,更新长度,重复2
否则往右再加,一直加到 又是大于等于S,重复2
3。走到数列结束,此时长度也更新到最小值,结束啦。。。。。。。

因为是从左走到右,所以时间复杂度为 O(n)
在这里插入图片描述

代码

代码,也可看做尺取法的模板


#include<iostream>

using namespace std;
const int maxn = 10005;
int n;
int S;
int a[maxn];

int main(){
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	int res =n+1; //长度 
	int s=0,t=0,sum=0;	//s:当前最左边位置	t:当前最右边位置
						// sum:是否大于等于S 
	for(;;){
		while(t<n && sum<S){
			sum += a[t++];
		}
		if(sum<S) break;  //即是累加,也是最后出口 
		res = min(res,t-s);
		sum -= a[s++];
	}
	
	if(res >n){		//不存在这样的子序列
		res =0; 	//输出0 
	}
	cout<<res<<endl;
	return 0;
}

再看这道,基本思路一样,
在这里插入图片描述

#include<iostream>
#include<set>
#include<map> 
using namespace std;

const int maxn = 1e6+5;
int p;
int a[maxn];

int main(){
	//读入 
	cin>>p;
	for(int i=0;i<p;i++){
		cin>>a[i];
	}
	//用set,可自动去重,排序 
	set<int> all;
	for(int i=0;i<p;i++){
		all.insert(a[i]);
	}
	int n=all.size();
	//尺取法
	int s=0,t=0,num=0; 
	map<int,int> count;
	int res=p;
	for(;;){
		while(t<p && num<n){
			if(count[a[t++]] ++== 0){ //如果count[a[t]]==0;那就加1;然后t++;		
							//一直加到num== 页数n 
				num++;              
			}
		}
		if(num<n) break;   //一直累加到num==n,或是到最后 循环出口
		res = min(res,t-s);
		if(-- count[a[s++]] == 0){  //把目前最前面的一个去掉 
			num--;					
		} 
	}
	cout<<res<<endl;
	return 0;
}
发布了75 篇原创文章 · 获赞 1 · 访问量 3628

猜你喜欢

转载自blog.csdn.net/A793488316/article/details/105187281