POJ 3061  Subsequence   尺取法   挑战146页

---恢复内容开始---

Subsequence
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 10487   Accepted: 4337

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

Source

 
题意:给你n个一连串的数字,求不小于m的最短的连续的数字长度;
分析:暴力的话复杂度n^2,肯定会超时,有两种方法,都要掌握,第一种是lower_bound函数的使用,lower_bound的复杂度是log(n)。所以该算法的复杂度是nlog(n),相比于暴力,算法的优化仅仅体现在使用了lower_bound将n变为了log(n).
#include<cstdio>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <algorithm>
#include <set>
using namespace std;
#define MM(a) memset(a,0,sizeof(a))
typedef long long LL;
typedef unsigned long long ULL;
const int mod = 1000000007;
const double eps = 1e-10;
const int inf = 0x3f3f3f3f;
long long mid,l,r,n,m;
int sum[100005];
int  main()
{
    int n,cas,s,k,ans,res;
    cin>>cas;
    while(cas--)
    {
        scanf("%d %d",&n,&s);
        sum[0]=0;ans=100005;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&sum[i]);
            sum[i]+=sum[i-1];   //将数组求和是常用的技巧
        }
        if(sum[n]<s)
        {
            cout<<"0"<<endl;
            continue;
        }
        for(k=0;sum[n]-sum[k]>=s;k++)  //枚举起点
            {
                res=lower_bound(sum+k,sum+n+1,sum[k]+s)-(sum+k);  //第一个大于等于该值的位                                          //置
                if(res<ans)
                    ans=res;
            }
        printf("%d
",ans);
    }
    return 0;
}

  下面重点介绍尺取法:

  尺取法的核心思想:假设当前a[s]+s[s+1]+...a[t]是最初>=sum的,那么当s变为s+1时,即a[s+1]+s[s+2]+...a[t+n]要想仍然成为最初>=sum的,则t+n>=t(a[s]可能为0),依据这一思想,可以设置两个”指针“,一个指向p一连续序列的开头位置,另一头q指向结尾位置,该子序列之和>=sum,则当p向右推移时,则q也应向右推移直到两指针之间的序列之和再次最初>=sum,复杂度是O(n),其实觉得尺取法最显著的优势就在于能够保存中间一部分子序列的计算结果,这也是其复杂度更低的原因。

#include<cstdio>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <algorithm>
#include <set>
using namespace std;
#define MM(a) memset(a,0,sizeof(a))
typedef long long LL;
typedef unsigned long long ULL;
const int mod = 1000000007;
const double eps = 1e-10;
const int inf = 0x3f3f3f3f;
long long mid,l,r,n,m;
int a[100005];
int  main()
{
    int n,cas,s;
    cin>>cas;
    while(cas--)
    {
        scanf("%d %d",&n,&s);
        a[0]=0;a[n+1]=0;
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        int p=0,q=0,v=0,ans=n+1;
        while(v<s&&q<=n)
            {
                q++;
                v+=a[q];
            }
        if(q==n+1)
            {
              cout<<"0"<<endl;
              continue;
            }
        for(;;)
            {
               while(v<s&&q<=n)
               {
                   q++;
                   v+=a[q];
               }
               if(q==n+1)
                 break;
               if(ans>q-p)
                   ans=q-p;
               p++;
               v-=a[p];
            }
        printf("%d
",ans);
    }
    return 0;

 

  

原文地址:https://www.cnblogs.com/smilesundream/p/5122364.html