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
2
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;
}