PAT A1044 二分查找

1044. Shopping in Mars (25)

题意

给出一个序列和一个定值M,要求找出和为M的子串,如果没有这样的子串,那么要求找出和大于M且和最小的子串

标准输入

16 15
3 2 1 5 4 6 8 7 16 10 15 11 9 12 14 13

标准输出

1-5
4-6
7-8
11-11

思路


  1. 其实这是一个求子列和的问题,不过有一些限制,不必列举所有的子列
  2. 以第i位(1≤i≤N)作为开头的有N个子串,而其中至多只有一个子串是符合条件的
    因为:子串和是单调递增的(所有项都是正整数),而要求是找到和大于等于给定数字,且和最小的子串,因此第一个使和达到要求的子串便是以这位数为开头的符合要求的子串

Example
16 15
3 2 1 5 4 6 8 7 16 10 15 11 9 12 14 13
以i=1第一位数3开头为例,要求的值是15,不断往后加,3+2+1+5+4 此时是首次达到大于等于15这个要求,而继续往后加必然是不满足最小这个要求的,因此3开头的子列只有这一个,然后继续看下一位2开头的子列。

  • 以这种算法的话,最坏情况下复杂度是O(n²),而这道题对运行时间有严格要求,如果将序列改写成增量数组的话,便可以使数组成为有序的,因此可以用二分查找,使程序时间复杂度降到O(nlgn)

  • 增量数组

    原序列
    3 2 1 5 4 6 8
    增量序列
    3 5 6 11 15 21 29

  • 实现代码

    #include <iostream>
    #include <string>
    #include <vector>
    #include <queue>
    #include <cmath>
    #include <cstdio>
    #include <array>
    #include <algorithm>
    #include <set>
    #include <vector>
    using namespace std;
    
    pair<pair<int, int>, int> Binary_search(int beg, int bottom, const vector<int>& v)
    {
        int left = beg, right = v.size() - 1;
        while (left < right)
        {
            int mid = (left + right) / 2;
            if (v[mid] - v[beg - 1] >= bottom)
            {
                right = mid;
            }
            else
            {
                left = mid + 1;
            }
        }
        return { { beg,right },v[right] - v[beg - 1] };
    }
    
    int main()
    {
    
        int N, M;
        int min;
        vector<int> vec_sum;
        vector<pair<int, int>> res;
    
        cin >> N >> M;
        vec_sum.resize(N + 1);
    
        for (size_t i = 1; i <= N; i++)
        {
            scanf("%d", &vec_sum[i]);
            vec_sum[i] += vec_sum[i - 1];
        }
    
        min = vec_sum[N];
        for (size_t i = 1; i <= N; i++)
        {
            auto fback = Binary_search(i, M, vec_sum);
            if (fback.second >= M)
            {
                if (fback.second == min)
                {
                    res.push_back(fback.first);
                }
                else if (fback.second < min)
                {
                    min = fback.second;
                    res.clear();
                    res.push_back(fback.first);
                }
            }
        }
    
        for (auto p : res)
        {
            printf("%d-%d\n", p.first, p.second);
        }
    
        system("pause");
        return 0;
    }
    

    PS
    对于这种运行时间要求严格的题(本题为100ms),输入输出最好使用scanf和printf而不是cin和cout,同样的算法,即使是关闭了同步,cin、cout仍然可能超时

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值