小白书49页
题意:
给你一个正整数序列,问你在里面找到一个最短的子序列,要求子序列的和大于等于k,输出序列长度。
1:很容易想到n三方的暴力,枚举序列左右端点,然后算和。
2:我们可以预处理前缀和,然后程序复杂度就变为O(n^2)
3:我们可以枚举右端点,然后二分左端点,然后复杂度变为O(nlogn)
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
int a[123456];
int main()
{
int n,m;
while(cin>>n>>m)
{
a[0]=0;
for(int i=1;i<=n;i++)
{
int t;
cin>>t;
a[i]=t+a[i-1];
}
int ans=99999999;
for(int i=1;i<=n;i++)
{
if(a[n]-a[i-1]>=m)
{
int l=upper_bound(a+1,a+1+n,m+a[i-1])-a;
// printf("%d\n",l-i+1);
ans=min(ans,l-i+1);
}
else break;
}
if(ans==99999999)cout<<0<<endl;
else cout<<ans<<endl;
}
return 0;
}
4:我们可以用双指针法(又叫尺取法),不断右移两个指针,程序复杂度变为O(n)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int MAXN=100000+10;
int sum[MAXN];
int main()
{
int n,s,t;
while(scanf("%d%d",&n,&s)!=EOF)
{
sum[0]=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&t);
sum[i]=sum[i-1]+t;
}
int ans=n+1,j=0;
for(int i=1;i<=n;i++)
{
if(sum[i]-s<0)continue;
while(sum[i]-sum[j]>=s) j++;
ans=min(ans,i-j+1);
}
printf("%d\n",ans==n+1 ? 0:ans);
}
}