poj 3061 Subsequence
Description
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
The first line is the number of test cases. 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 no answer, print 0.
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个整数组成一个数列,从数列中找到一段连续的子序列使得这个子序列的和不小于s的最小长度。如果不存在,输出0.
如果子序列为[i,j)的和不小于s,即ai+ai1+ai2+...+aj>=s, 若存在j'>j,一定存在[i,j')的和不小于s,如果我们先预处理数组sum[],那么sum[j]-sum[i]>=s;
预处理只需要o(n)的时间复杂度,可以再O(1)的时间内算出这段区间的和。这样我们就可以确定起点i,然后二分搜索j满足sum[j]-sum[i]>=s;
-------------------------------------------------------------- Please stop to think and then to read next!! ------------------------------------------------
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;
const long M=100005;
long a[M];
long sum[M];
void solve(long n, long s){
if (sum[n]<s){
cout<<0<<endl;
return;
}
long res=n;
for (long i=0; sum[i]+s<=sum[n]; i++){
long t=lower_bound(sum+i, sum+n, sum[i]+s)-sum;/*low_bound的作用相当于二分搜索,不过又有些许不同,可以找度娘,也可以自己写一个二分搜索,具体做法可以参考http://blog.csdn.net/greetrix/article/details/37736121*/
res=min(res,t-i);
}
cout<<res<<endl;
}
void init(long n, long s)
{
memset(a,0,sizeof(a));
memset(sum, 0, sizeof(sum));
for (long i=0; i<n; i++)
scanf("%d", &a[i]);
for (long i=0; i<n; i++)
sum[i+1]=sum[i]+a[i];
}
int main()
{
int t;
scanf("%d",&t);
for (int i=1; i<=t; i++)
{
long s,n;
scanf("%ld %ld", &n, &s);
init(n,s);
solve(n,s);
}
return 0;
}
这个算法的时间复杂度是O(nlogn),已经能很好的处理这个问题,但是我们可以用到更优的算法,尺取法。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;
const long M=100005;
long a[M];
void solve(long n, long s){
long res=n+1;
long i=0;//起点
long j=0;//终点
long long sum=0;//和
for (; ;)
{
while (i<n && sum<s){
sum+=a[i++];
}//确定终点使a[i]+...+a[j]>=s;
if (sum<s) break;//如果全部值都加上还是小于s,无解。
res=min(res,i-j);//找最小。
sum-=a[j++];//减去a[j],从a[j+1]开始,j+1为起点。
}
if (res>n){
res=0;
}//还是保证最多取n个数。
cout<<res<<endl;
}
void init(long n, long s)
{
memset(a,0,sizeof(a));
for (long i=0; i<n; i++)
scanf("%d", &a[i]);
}
int main()
{
int t;
scanf("%d",&t);
for (int i=1; i<=t; i++)
{
long s,n;
scanf("%ld %ld", &n, &s);
init(n,s);
solve(n,s);
}
return 0;
}
时间复杂度是O(n),下面的图片知道两者的差距。