zoukankan      html  css  js  c++  java
  • [LeetCode]Count of Range Sum

    题目:Count of Range Sum

    Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusive.
    Range sum S(i, j) is defined as the sum of the elements in nums between indices i and j (i ≤ j), inclusive.

    Note:
    A naive algorithm of O(n2) is trivial. You MUST do better than that.

    Example:
    Given nums = [-2, 5, -1]lower = -2upper = 2,
    Return 3.
    The three ranges are : [0, 0][2, 2][0, 2] and their respective sums are: -2, -1, 2.

    题意:

    给定一个整数数组和一个范围,找到该数组的子集(要求子集是数组中连续的数)中满足子集元素的和在给定范围内的所有子集的个数。

    分析:

    正常的思路,需要找数组的不同的子集,复杂度为O(n^2)。

    /**
    动态规划:S[i][j]表示nums中从i到j的和
    S[i][j] = S[i][j - 1] + nums[j]
    **/
    int LeetCode::countRangeSum(vector<int>& nums, int lower, int upper){
        int n = nums.size();
        vector<vector<int>> sum(n,vector<int>(n, 0));
        int count = 0;
        for (size_t i = 0; i < n; i++){
            sum[i][i] = nums[i];
            if (sum[i][i] >= lower && sum[i][i] <= upper)++count;
        }
        for (size_t i = 0; i < n; i++){
            for (size_t j = i + 1; j < n; j++){
                sum[i][j] = sum[i][j - 1] + nums[j];
                if (sum[i][j] >= lower && sum[i][j] <= upper)++count;
            }
        }
        return count;
    }

    但是,它可以在O(nlogn)的复杂度中完成。

    设S(n)表示前n个元素的和,SubSet(i,j)表示数组i到j的子集的和;给定的范围是[low,up];

    则简单来讲,要找满足low<=SubSet(i,j)<=up的SubSet的个数。

    SubSet(i,j) = S(j) - S(i);也就是要求low<=S(j) - S(i)<=up中ij的个数。

    这是可以看出必然需要一个辅助数组保存前n个元素的和;

    简化要求来思考:当访问到新的元素j时,得到前j项和,则此时如果是只需求以j为终点的子集中满足题目要求的和的子集个数该怎么求呢?

    只需要遍历前j-1个前n项和然后用S(j)-S(k),判断是否在范围内即可;

    进一步,如果前j-1个前n项和是有序的,就只要找到第一个满足下界和最后一个满足上界的,然后他们之间包含多少个元素就有多少个满足要求的子集。

    这样就要求按大小顺序来存储前n项和,而且可以使用lower_bound()和upper_bound()函数来确定范围,于是就可以想到用multiset来存储前n项和。

    那么要找到所有的数组中子集满足条件的个数,就需要将上面求的每个位置的子集的数量加起来即可。

    /**
    一个基本的想法是使用multiset保存前i个元素的和.因为set是有序的可重复的容器.
    当前位置为i时的满足给定范围的序列应该是:lower=< sum[i]-sum[j]<= upper.它对应的范围是[j,i].
    所以只需要计算j的个数:使得j (0=< j< i) 满足 sum[i]-upper=< sum[j]<= sum[i]-lower.
    STL提供的函数upper_bound, lower_bound能够快速找到这样的j的范围,因为set有序范围内的一定满足上面的条件.
    set使用红黑树来实现,因此上面的操作时间复杂度O(logN).
    所以总的时间复杂度为O(NlogN).
    **/
    int LeetCode::countRangeSum(vector<int>& nums, int lower, int upper){
        multiset<long long>sums;//注意使用long long防止溢出
        long long sum = 0L;
        int result = 0;
        sums.insert(0);//插入第0个和
        for (size_t i = 0; i < nums.size(); i++){
            sum += nums[i];
            result += distance(sums.lower_bound(sum - upper), sums.upper_bound(sum - lower));//找到满足sum[i]-upper=< sum[j]<= sum[i]-lower的范围
            sums.insert(sum);
        }
        return result;
    }
  • 相关阅读:
    (转载)VS2010/MFC编程入门之十九(对话框:颜色对话框)
    (转载)VS2010/MFC编程入门之十八(对话框:字体对话框)
    (转载)VS2010/MFC编程入门之十七(对话框:文件对话框)
    (转载)VS2010/MFC编程入门之十六(对话框:消息对话框)
    VS2010/MFC编程入门之十四(对话框:向导对话框的创建及显示)
    (转载)VS2010/MFC编程入门之十五(对话框:一般属性页对话框的创建及显示)
    (转载)VS2010/MFC编程入门之十三(对话框:属性页对话框及相关类的介绍)
    Android笔记之AsyncTask
    Android笔记之线程使用
    Android笔记之活动指示器使用
  • 原文地址:https://www.cnblogs.com/yeqluofwupheng/p/6959377.html
Copyright © 2011-2022 走看看