题目:

Subsequence (POJ No.3061)

给定长度为n的数列整数a,*,an,以及整数S。求出总和不小于S的连续子序列的长度的最小值。如果解不存在,则输出0。

④限制条件

●10<n< 10^5

●0<aj≤10^4

●S< 10^8

输入


n=10

s=15

a = {5,1,3,5,10,7, 4, 9,2,8}


输出


2 (5+10)


输入


n=5

S=11

a= {1,2,3,4,5}


输出


3 (3+4+5)


由于所有的元素都大于零,如果子序列[s, t)满足as+..+a(t-1)≥s,那么对于任何的t<t'一定有as+...+a(t'-1)≥S。此外对于区间[s,t)上的总和来说如果令

sum(i)=a0+a1+..+a(i-1)

那么

as+a(s+1)+..+a(t-1)=sum(t)-sum(s)

因此预先以O(n)的时间计算好sum的话,就可以以O(1)的时间计算区间上的总和。这样一来,子序列的起点s确定以后,便可以用二分搜索快速地确定使序列和不小于S的结尾的最小值。

这个算法的复杂度是O(nlogn),虽然足以解决这个问题,但我们还可以更加高效地求解。我们设以as,开始总和最初大于S时的连续子序列为+..+ar-r,这时

Subsequence (POJ No.3061)_Subsequence

所以从a++开始总和最初超过S的连续子序列如果是

Subsequence (POJ No.3061)_复杂度_02

的话,则必然有t≤t'利用这一-性

质便可以设计出如下算法:

  • (1)以s=1= sum= 0初始化。
  • (2)只要依然有sum<S,就不断将sum增加at,并将t增加1。
  • (3)如果(2)中无法满足sum≥S则终止。否则的话,更新res = min(res, t-s)。
  • (4)将sum减去as,s增加1然后回到(2)。

 

对于这个算法,因为最多变化n次,因此只需O(n)的复杂度就可以求解这个问题了。

Subsequence (POJ No.3061)_复杂度_03

实现:

#include<iostream>
#include<vector>
using namespace std;
vector<int> A;
int main() {
	int n,S;
	cin>>n>>S;
	A.resize(n);
	for(int i=0; i<n; ++i)
		cin>>A[i];
	int res=n+1;
	int s=0,t=0,sum=0;
	while(1) {
		while(t<n&&sum<S)
			sum+=A[t++];
		if(sum<S)
			break;
		res=min(res,t-s);
		sum-=A[s++];
	}
	res=(res>n?0:res);
	cout<<res;
	return 0;
}