題目:
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,這時
所以從a++開始總和最初超過S的連續子序列如果是的話,則必然有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)的複雜度就可以求解這個問題了。
實現:
#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;
}