POJ 3061 Subsequence (求总和不小于S的连续子序列的长度的最小值)

Subsequence POJ - 3061

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

题意:

求出总和不小于S的连续子序列的长度的最小值。

解题:

1.一开始的时候,这里使用的是暴力,结果t掉了(超时)。

#include<iostream>
#include<cstring>
using namespace std;

int a[100010];
int summ[100010];

int main(){
	int t;cin>>t;
	int n,s;
	while(t--){
		cin>>n>>s;
		memset(a,0,sizeof(a));
		for(int i=1;i<=n;i++){
			cin>>a[i];summ[i]=summ[i-1]+a[i];
			if(a[i]>=s) goto L1;
		}
		int l,j,k;
		for(l=2;l<=n;l++){
			for(j=1;j+l<=n;j++){
				int tmp=0;
				/*
				for(k=0;k<l;k++){
					tmp+=a[j+k]; 
					if(tmp>s) goto L1;
				}
				*/
				tmp=summ[j+l]-summ[j];
				if(tmp>=s) goto L1;
			}
		}
		//首先最简单的想法,每个加起来试下,两个循环,n^2
		//我:用长度l当循环,l=1,l=2.从小往下试 
		//这个地方可以改进成前缀和 
		L1:printf("%d\n",l);
		//cout<<t<<endl;
	}
	return 0;
} 

ps:好像这个goto语法是禁忌之术,哈哈哈,但其实我感觉这个是很好的一个直接跳出多层循环的方法

2.再次进行做的时候,是在网上寻找了思路

讲一下思路吧,就还是保留一个前缀和,用for循环,定住左边,用summ[j]+s去找右侧,
用upper_bound的方法去找到右侧,
然后左侧的j++,直到找到最短的一个长度

(因题干中是不小于,故我用upper找到右侧必定大于的那个下标,这样就不用再考虑如果等号成立了对长度表示的影响了)

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

const int N=1e5+5;

int a[N];
int summ[N];

int main(){
	int t;cin>>t;
	int n,s;
	while(t--){
		cin>>n>>s;
		int ans=1;
		for(int i=1;i<=n;i++){
			cin>>a[i];summ[i]=summ[i-1]+a[i];
			if(a[i]>=s) goto L1;//这里是有一项超过就停止。 
		}
		if(summ[n]<s) {
			cout<<0<<endl;//整个序列都不行,既没有答案,输出0 
			continue;//跳过这次循环 
		}
		int l,j,k;
		//这次思路,定住右边,再定左边
		ans=n;
		for(int j=1;summ[n]-summ[j]>n;j++){//只有在这个条件下,才可以一直找下去 
			if(summ[n]-summ[j]>s){//在j,n中,就开始找
				int t=upper_bound(summ+j,summ+n,summ[j]+s)-summ;//在j,n之间找右侧大于s的数,
				ans=min(ans,t-j); 
			}
		} 
		
		L1:printf("%d\n",ans);
		
	}
	
	return 0;
} 

3.这里还有个做法,是大家使用的尺分法:

感觉就是先找左边第一个点,然后找其最右端。
再移动左边i++,
再找第二个点的满足条件的最右端
直到区间内元素和小于s时,结束

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

const int N=1e5+5;

int a[N];


int main(){
	int t;cin>>t;
	int n,s,ans;
	int l,r,sum;
	while(t--){
		sum=0;
		cin>>n>>s;
		for(int i=1;i<=n;i++){
			cin>>a[i];
			if(a[i]>=s) {
				ans=1;
				goto L1;//这里是有一项超过就停止。 
			}
		}
		l=1,r=1;//左右都从头出发,(因为序列是从左向右进行,额,,这是强行说辞罢了。。 
		ans=n+1;
		
		while(1){
			while(r<=n&&sum<s){//注意! r<=n,r表示右端,则可以达到 
				sum+=a[r++];
			}
			if(sum<s){
				break;
			}
			ans=min(ans,r-l);
			sum-=a[l++];
		}	
	
	L1:
		if(ans>n){
			cout<<0<<endl;
		}
		else cout<<ans<<endl;
	}
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值