poj 3061 Subsequence

                                          poj 3061 Subsequence

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
原文的大意是给定n个整数组成一个数列,从数列中找到一段连续的子序列使得这个子序列的和不小于s的最小长度。如果不存在,输出0.
如果子序列为[i,j)的和不小于s,即ai+ai1+ai2+...+aj>=s, 若存在j'>j,一定存在[i,j')的和不小于s,如果我们先预处理数组sum[],那么sum[j]-sum[i]>=s;
预处理只需要o(n)的时间复杂度,可以再O(1)的时间内算出这段区间的和。这样我们就可以确定起点i,然后二分搜索j满足sum[j]-sum[i]>=s;
 -------------------------------------------------------------- Please stop to think and then to read next!!   ------------------------------------------------

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;
const long M=100005;
long a[M];
long sum[M];
void solve(long n, long s){
	if (sum[n]<s){
	 cout<<0<<endl;
	 return;
    }
    long res=n;
    for (long i=0; sum[i]+s<=sum[n]; i++){
    	long t=lower_bound(sum+i, sum+n, sum[i]+s)-sum;/*low_bound的作用相当于二分搜索,不过又有些许不同,可以找度娘,也可以自己写一个二分搜索,具体做法可以参考http://blog.csdn.net/greetrix/article/details/37736121*/
    	res=min(res,t-i);
    }
    cout<<res<<endl;
	
}
void init(long n, long s)
{
	memset(a,0,sizeof(a));

	memset(sum, 0, sizeof(sum));	 
	for (long i=0; i<n; i++)
	scanf("%d", &a[i]);
	for (long i=0; i<n; i++)
	sum[i+1]=sum[i]+a[i];		
}
int main()
{
	int t;
	scanf("%d",&t);
	for (int i=1; i<=t; i++)
	{
		long s,n;
		scanf("%ld %ld", &n, &s);
		init(n,s);
		solve(n,s);	
	}
	return 0;
}



这个算法的时间复杂度是O(nlogn),已经能很好的处理这个问题,但是我们可以用到更优的算法,尺取法。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;
const long M=100005;
long a[M];
void solve(long n, long s){
	long res=n+1;
	long i=0;//起点
	long j=0;//终点	
	long long sum=0;//和
	for (; ;)
	{
		while (i<n && sum<s){
			sum+=a[i++];
		}//确定终点使a[i]+...+a[j]>=s;
		if (sum<s) break;//如果全部值都加上还是小于s,无解。
		res=min(res,i-j);//找最小。
		sum-=a[j++];//减去a[j],从a[j+1]开始,j+1为起点。	
	}
	if (res>n){
		res=0;
	}//还是保证最多取n个数。
	cout<<res<<endl;
}
void init(long n, long s)
{
	memset(a,0,sizeof(a));	 
	for (long i=0; i<n; i++)
	scanf("%d", &a[i]);	
}
int main()
{
	int t;
	scanf("%d",&t);
	for (int i=1; i<=t; i++)
	{
		long s,n;
		scanf("%ld %ld", &n, &s);
		init(n,s);
		solve(n,s);	
	}
	return 0;
}
时间复杂度是O(n),下面的图片知道两者的差距。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值