Time Limit: 1000MS |
|
Memory Limit: 65536K |
Total Submissions: 10484 |
|
Accepted: 4335 |
Description
Input
Output
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
题意:给定长度为n的数列整数a0,a1,a2....an-1;以及整数S,求出总和不小于S的连续子序列的长度的最小值。如不存在输出0。
题解:n最大值为100000,显然O(n^2)算法超时,我们可以用二分查找或尺取法解决问题。
二分查找,时间复杂度为O(nlogn):
sumi=a0+a1+...+a[i],则我们只要枚举sum[t]-sum[s]>=S,记录下最小的t-s的值就能得出解了。
代码如下:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int sum[100010]; int main() { int n,S,t,i,j,ans,a; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&S); memset(sum,0,sizeof(sum)); scanf("%d",&sum[0]); for(i=1;i<n;++i) { scanf("%d",&a); sum[i]=sum[i-1]+a; } if(sum[n-1]<S) { printf("0\n"); continue; } ans=n; for(i=0;sum[i]+S<=sum[n-1];++i) { int t=lower_bound(sum+i,sum+n,sum[i]+S)-sum; ans=min(ans,t-i); } printf("%d\n",ans); } return 0; }
尺取法,时间复杂度为O(n):
下面重点介绍尺取法。
尺取法:反复推进区间的开头和结尾,并求取满足最小条件的最小区间。
我们设以as开始的总和最初大于S时的连续子序列为as+......+at-1,这时有: as+1+....+at-2 < as+...+at-2<S
所以从as+1开始总和最初超过S的连续子序列如果是as+1+...+at'-1的话,则必然有t<=t'。利用这一性质可以设计出如下算法:
① 以s=t=sum=0初始化。
② 只要有sum<S,就不断将sum增加at,并将t增加1。
③ 如果②中无法满足sum>=S则终止。否则的话,更新ans=min(ans,t-s)。
④ 将sum减去as,s增加1然后回到②。
代码如下:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int a[100010]; int main() { int t,n,S,i,sum,start,end,ans; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&S); for(i=0;i<n;++i) scanf("%d",&a[i]); sum=a[0]; start=0; end=0; ans=n+1; while(1) { while(end<n-1&&sum<S) { sum+=a[++end]; } if(sum<S) break; ans=min(ans,end-start+1); sum-=a[start]; start++; } if(ans>n) printf("0\n"); else printf("%d\n",ans); } return 0; }