zoukankan      html  css  js  c++  java
  • POJ:3061-Subsequence(尺取法模板详解)

    Subsequence

    Time Limit: 1000MS Memory Limit: 65536K
    Total Submissions: 18795 Accepted: 8043

    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


    解题心得:

    1. 题意就是给你n个数,要你选择连续的一段区间,其总和不小于s,问你最短的区间长度是多少。
    2. 尺取法的模板题,尺取法很有意思,也被称之为尺取虫,这个可以说是很形象了,可以看成是一个一定长度的虫在线性表中爬动,复杂度就是O(N)。关于尺取有很多种写法,不同的写法复杂度不同,这里讲三种写法
      • 第一种就是二分加尺取法,首先二分出一个长度,然后用尺取法去数组中检验这个二分出来的总和,复杂度为O(NlogN)。
      • 第二种感觉和尺取关系不大,用的是二分,先求出前缀和,以每一个前缀和为起点,当前前缀和+s为终点,因为前缀和是一个递增的数列,所以就可以二分查找终点,然后得到长度。复杂度为O(NlogN)。
      • 第三种就是尺取,只不过这个尺取虫的长度是可以变换的,我们找一个最小的长度就行了,从起点开始每次保证尺取范围的总和不小于s,同时向后面移动。复杂度为O(N).

    第一种尺取代码

    #include <algorithm>
    #include <iostream>
    #include <stdio.h>
    using namespace std;
    const int maxn = 1e5+10;
    
    int num[maxn],n,s;
    
    void init() {
        scanf("%d%d",&n,&s);
        for(int i=0;i<n;i++)
            scanf("%d",&num[i]);
    }
    
    bool checke(int len) {
        int sum = 0;
        for(int i=0;i<len;i++)
            sum += num[i];
        if(sum >= s)
            return true;
        int l = 0,r = len-1;
        while(r < n) {//
            sum -= num[l];
            l++,r++;
            sum += num[r];
            if(sum >= s)
                return true;
        }
        return false;
    }
    
    int binary_search() {//二分尺取虫的长度
        int l = 1 ,r = n;
        while(r - l > 1) {
            int mid = (l + r) >> 1;
            if(checke(mid))
                r = mid;
            else
                l = mid;
        }
        return r;
    }
    
    bool check_ans() {
        int sum = 0;
        for(int i=0;i<n;i++)
            sum += num[i];
        if(sum < s)
            return true;
        return false;
    }
    
    int main() {
        int t;
        scanf("%d",&t);
        while(t--) {
            init();
            if(check_ans()) {//如果总和都小于s直接输出0
                printf("0
    ");
                continue;
            }
            int ans = binary_search();
            printf("%d
    ", ans);
        }
        return 0;
    }

    第二种写法代码:

    #include <algorithm>
    #include <iostream>
    #include <stdio.h>
    using namespace std;
    const int maxn = 1e5+10;
    
    int sum[maxn],n,s;
    
    void init() {
        scanf("%d%d",&n,&s);
        for(int i=1;i<=n;i++) {
            int temp;
            scanf("%d",&temp);
            sum[i] = sum[i-1]+temp;
        }
    }
    
    int solve() {
        int ans = 0x7f7f7f7f;
        for(int i=0;sum[i]+s<=sum[n];i++) {
            int last = sum[i] + s;
            int pos = lower_bound(sum+i,sum+n+1,last) - sum;//就是一个二分查找
            int len = pos - i;
            ans = min(ans,len);
        }
        return ans;
    }
    
    int main() {
        int t;
        scanf("%d",&t);
        while(t--) {
            init();
            int ans = solve();
            if(sum[n] < s)
                ans = 0;
            printf("%d
    ",ans);
        }
        return 0;
    }

    第三种写法代码:

    #include <algorithm>
    #include <iostream>
    #include <stdio.h>
    using namespace std;
    const int maxn = 1e5+10;
    
    int n,s,num[maxn];
    
    int main() {
        int t;
        scanf("%d",&t);
        while(t--) {
            int sum,l,r,ans;
            sum = l = r = 0;
            ans = 0x7f7f7f7f;
            scanf("%d%d", &n, &s);
            for (int i = 0; i < n; i++)
                scanf("%d", &num[i]);
            while (1) {
                while (sum < s && r < n) {
                    sum += num[r++];
                }
                if (sum < s)
                    break;
                ans = min(ans, r - l);
                sum -= num[l++];
            }
            if (ans > n)
                ans = 0;
            printf("%d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    由typedef和函数指针引起的危机
    从JVM角度看Java多态
    C语言中判断字符串str1是否以str2开始或结束
    Linux下利用json-c从一个json数组中提取每一个元素中的部分字段组成一个新json数组
    C语言中的条件编译
    学会 Python 到底能干嘛?我们整理出了 7 大工作方向……
    新手指南:我应该学哪种编程语言?
    盘点:2019年最赚钱的10种编程语言
    11个提升编程能力的小方法
    收好这份 Vue 升级图,假期偷偷上个钻
  • 原文地址:https://www.cnblogs.com/GoldenFingers/p/9107114.html
Copyright © 2011-2022 走看看