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