【Jason's_ACM_解题报告】Subsequence

该博客介绍了Subsequence问题,要求找到序列中连续子序列的最小长度,其和大于或等于给定的目标值。博主列举了三种解法,包括使用二分查找的两种O(NlogN)方法和一种线性时间复杂度O(N)的方法,并详细解释了每种方法的思路。还特别提到了lower_round函数在二分查找中的应用。
摘要由CSDN通过智能技术生成

Subsequence

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

Many test cases will be given. 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 there isn't such a subsequence, print 0 on a line by itself.


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


Sample Output

3


我觉得这道题非常经典,一共有三种解法。三种解法都需要先求出前缀和来。

第一种,Liu未提及的,我二分子序列的长度,二分性质就不用证明了吧,很显然的一件事情。check函数线性枚举所有长度为(L+R)>>1的子序列,若没有满足条件的返回false,只要有其一即返回true。时间复杂度为O(NlogN)。

第二种,Liu的思想是枚举每一个子序列的右端点,然后二分子序列的左端点,check条件式满足条件的最大下标左端点。Liu给出的是利用lower_round函数来求,lower_round函数内部是用二分法实现的,所以时间复杂度和第一种一样。

第三种,对于等式SUM[i-1]<=SUM[j]-S来说(因为SUM数组为前缀和,所以SUM数组是递增的),不仅左端点递增,右端点也是递增的,所以可以直接枚举右端点,左端点不用反复枚举,因为,假设[i,j]为当前枚举的符合条件的子序列,由于j是向后枚举,所以若左端点i不动或向左移,则子序列的和必定增加,不符合最优,所以左端点i只有向靠近右端点的方向移动财富和最优的性质,所以这个算法事件复杂度为O(N)。


下面介绍一下lower_round函数:





附代码如下:

第一种:

#include<cstdio> 

using namespace std;

#define MAXN (100000+5)

int sum[MAXN];
int n,s;

bool check(int x){
	for(int i=x;i<=n;i++){
		if(sum[i]-sum[i-x]>=s)return true;
	}
	return false;
}

int main(){
	while(scanf("%d%d",&n,&s)!=EOF){
		sum[0]=0;
		for(int i=1;i<=n;i++){
			int x;
			scanf("%d",&x);
			sum[i]=sum[i-1]+x;
		}
		int L=0,R=n+1;
		while(L<R){
			int MID=((L+R)>>1);
			if(check(MID))R=MID;
				else L=MID+1;
		}
		if(R==n+1)L=0;
		printf("%d\n",L);
	}
	return 0;
}

第二种:

#include<cstdio> 
#include<algorithm>

using namespace std;

#define MAXN (100000+5)

int a[MAXN];

int main(){
	int n,s;
	while(scanf("%d%d",&n,&s)!=EOF){
		a[0]=0;
		for(int i=1;i<=n;i++){
			int x;
			scanf("%d",&x);
			a[i]=a[i-1]+x;
		}
		int ans=n+1;
		for(int j=1;j<=n;j++){
			int i=lower_bound(a,a+j,a[j]-s)-a;
			if(i>0)ans=min(ans,j-i+1);
		}
		ans=ans==n+1?0:ans;
		printf("%d\n",ans);
	}
	return 0;
}

第三种:

#include<cstdio> 
#include<algorithm>

using namespace std;

#define MAXN (100000+5)

int a[MAXN];

int main(){
	int n,s;
	while(scanf("%d%d",&n,&s)!=EOF){
		a[0]=0;
		for(int i=1;i<=n;i++){
			int x;
			scanf("%d",&x);
			a[i]=a[i-1]+x;
		}
		int ans=n+1;
		int i=1;
		for(int j=1;j<=n;j++){
			if(a[i]>a[j]-s)continue;
			while(a[i]<=a[j]-s)i++;
				ans=min(ans,j-i+1);
		}
		ans=ans==n+1?0:ans;
		printf("%d\n",ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值