POJ 3061 Subsequence 尺取法

http://poj.org/problem?id=3061

Subsequence

Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 28853 Accepted: 12121

Description

A sequence of N positive integers (10 < N < 100 000), each of them less than or equal 10000, and a positive integer S (S < 100 000 000) are given. Write a program to find the minimal length of the subsequence of consecutive elements of the sequence, the sum of which is greater than or equal to S.

Input

The first line is the number of test cases. For each test case the program has to read the numbers N and S, separated by an interval, from the first line. The numbers of the sequence are given in the second line of the test case, separated by intervals. The input will finish with the end of file.

Output

For each the case the program has to print the result on separate line of the output file.if no answer, print 0.

Sample Input

2
10 15
5 1 3 5 10 7 4 9 2 8
5 11
1 2 3 4 5

Sample Output

2
3

Source

Southeastern Europe 2006

参考:https://blog.csdn.net/lxt_Lucia/article/details/81091597

尺取法:顾名思义,像尺子一样取一段,借用挑战书上面的话说,尺取法通常是对数组保存一对下标,即所选取的区间的左右端点,然后根据实际情况不断地推进区间左右端点以得出答案。尺取法比直接暴力枚举区间效率高很多,尤其是数据量大的时候,所以说尺取法是一种高效的枚举区间的方法,是一种技巧,一般用于求取有一定限制的区间个数或最短的区间等等。当然任何技巧都存在其不足的地方,有些情况下尺取法不可行,无法得出正确答案,所以要先判断是否可以使用尺取法再进行计算。

 

使用尺取法时应清楚以下四点:

1、  什么情况下能使用尺取法?  2、何时推进区间的端点? 3、如何推进区间的端点? 4、何时结束区间的枚举?

 

    尺取法通常适用于选取区间有一定规律,或者说所选取的区间有一定的变化趋势的情况,通俗地说,在对所选取区间进行判断之后,我们可以明确如何进一步有方向地推进区间端点以求解满足条件的区间,如果已经判断了目前所选取的区间,但却无法确定所要求解的区间如何进一步得到根据其端点得到,那么尺取法便是不可行的。首先,明确题目所需要求解的量之后,区间左右端点一般从最整个数组的起点开始,之后判断区间是否符合条件在根据实际情况变化区间的端点求解答案。

    现在我们先举个栗子并分析一下尺取法的过程叭~

    以下是几个经典的使用尺取法的例题,都是从挑战书上引用的。我们来挨个分析一下。(尺取法通常会需要对某些量进行预处理,以便能在使用时快速地判断。)

 

1、  Poj3061

题意:给定一个序列,使得其和大于或等于S,求最短的子序列长度。

分析:首先,序列都是正数,如果一个区间其和大于等于S了,那么不需要在向后推进右端点了,因为其和也肯定大于等于S但长度更长,所以,当区间和小于S时右端点向右移动,和大于等于S时,左端点向右移动以进一步找到最短的区间,如果右端点移动到区间末尾其和还不大于等于S,结束区间的枚举。

这个题目区间和明显是有趋势的:单调变化,所以根据题目要求很容易求解,但是在使用之间需要对区间前缀和进行预处理计算。

#include<iostream>
#include<cstring>
using namespace std;
int a[100005],sum[100005];
int main()
{
	int k,n,m,t=0;
	cin >>k;
	while(k--)
	{
		memset(a,0,sizeof(a));
		memset(sum,0,sizeof(sum));
		int l=0,r=0;
		cin >> n >> m;
		for(int i = 1; i <= n; ++i)//计算前缀和 
		{
			cin >> a[i];
			sum[i] = sum[i-1] + a[i];
		}
		bool p = 0;
		int min=0xffffff;
		while(l <= r && r <= n+1)
		{
			if(sum[r]-sum[l] >= m) {//如果差大于等于m,就缩小范围 
				l++;
				p=1;
				if(r-l+1 <= min) min = r-l+1;
			}
			else if(sum[r] - sum[l] < m) //否则扩大范围 
			{
				r++;
			}
		}
		if(p) cout << min << endl;
		else cout << "0" << endl;
	}
	
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值