尺取法 poj3061

尺取法:就是两个指针表示区间[l,r]的开始与结束然后根据题目来将端点移动,是一种十分有效的做法。适合连续区间的问题

1 poj3061

  给定长度为n的数列整数a0,a1,a2,a3 ..... an-1以及整数S。求出和不小于S的连续子序列的长度的最小值。如果解不存在,则输出0。

方法1;暴力:O(n^3枚举)

方法2:通过预先计算sum[i],那么子序列的和就可以在O(1)内找到

注意这题是positive integer,那么sum肯定是递增的,对于每个位置,通过二分搜索找到S-sum[i]<sum[k]的最小k,O(nlogn)

方法3:尺取法:我们若有a[s]+...a[t-1]>S

分析:我们知道连续子序列的和可以在输入的时候获取sum[i],那么i+1到j的连续序列和就可以用sum[j]-sum[i]获得了

主要思想为:当a1,  a2  , a3 满足和>=S,得到一个区间长度3,那么去掉开头a1,   剩下 a2,a3,判断是否满足>=S,如果满足,那么区间长度更新,如果不满足,那么尾部向后拓展,判断a2,a3,a4是否满足条件。重复这样的操作。
个人对尺取法的理解:当一个区间满足条件时,那么去掉区间开头第一个数,得到新区间,判断新区间是否满足条件,如果不满足条件,那么区间末尾向后扩展,直到满足条件为之,这样就得到了许多满足条件的区间,再根据题意要求什么,就可以在这些区间中进行选择,比如区间最长,区间最短什么的。这样跑一遍下来,时间复杂度为O(n)。

<span style="font-size: 14px;">#include <iostream>
#include <cstdio>
#include <iostream>
using namespace std;	
const int maxn=100005;
int a[maxn];
int sum[maxn];
int main(int argc, char const *argv[])
{
	int t,n,s,left,right;
	cin>>t;
	while(t--){
		sum[0]=0;
		cin>>n>>s;
		int ans=n+1;
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			sum[i]=sum[i-1]+a[i];
		}
		for(left=0,right=1;left<n;left++){
			while(right<=n&&sum[right]-sum[left]<s){
				right++;
			}//the sum of left+1 to right
			if(right==n+1) break;
			if(right-left<ans) ans=right-left;
		}
		if(ans==n+1) cout<<"0"<<endl;
		else cout<<ans<<endl;
	}

	return 0;
}</span><span style="font-size: 16px;">
</span>



你可能感兴趣的:(尺取法 poj3061)