【问题标题】:Subsequence with maximum sum in array of ints [duplicate]整数数组中具有最大总和的子序列[重复]
【发布时间】:2014-06-11 17:24:30
【问题描述】:

给定一个整数数组,如何找到两个索引 i 和 j,使得子数组中从索引开始和结束的元素的总和在线性时间内最大化?

【问题讨论】:

  • 你的意思是 - 在索引之间?
  • i = 0j = array.length-1 :)
  • @Bart,谁说数组元素大于零?
  • 到目前为止的答案似乎假设您的意思是“从索引 i 到索引 j 的元素总和”,但据我所知,您只要求元素 i 和 j 的总和,请详细说明? (也可以 i == j 吗?在这种情况下答案将是 2 * max(array values) :-))
  • 中间元素的总和。还有负面因素

标签: algorithm arrays language-agnostic


【解决方案1】:

简单。假设给定数组a。首先,计算数组s,其中s[i] = a[0]+a[1]+...+a[i]。您可以在线性时间内完成:

s[0]=a[0];
for (i=1;i<N;i++) s[i]=s[i-1]+a[i];

现在,a[i]+a[i+1]+..+a[j] 的总和等于 s[j]-s[i-1]。对于一个固定的j,为了最大化这个差异的值,你应该在0..(j-1)的范围内找到一个最小的s[i-1]

想象一个在数组中找到最小值的常用算法。

min = x[0];
for (j=1; j<N; j++)
  if (x[j] < min)
    min = x[j];

您迭代并将每个数组元素与min 进行比较...但是在每次迭代中,min 是数组中的最小值,其中索引范围为0..j!这就是我们正在寻找的!

global_max = a[0];
max_i = max_j = 0;
local_min_index = 0;
for (j=1; j<N; j++){
  // here local_min is the lowest value of s[i], where 0<=i<j
  if (s[j] - s[local_min_index] > global_max) {
     global_max = s[j] - s[local_min_index]
     //update indices
     max_i = local_min_index + 1;
     max_j = j;
  }
  //update local_min_index for next iteration
  if (s[j]<local_min){
    local_min = s[j];
    // update indices
    local_min_index = j;
  }
}

【讨论】:

  • 感谢一位匿名用户的冗长帖子,解释了代码中的错误。我摆脱了它们,除了 N==0 失败。
【解决方案2】:

来自我的programming pearls

maxsofar = 0
maxendinghere = 0
for i = [0, n)
    /* invariant: maxendinghere and maxsofar are accurate
       are accurate for x[0..i-1] */
    maxendinghere = max(maxendinghere + x[i], 0)
    maxsofar = max(maxsofar, maxendinghere)

【讨论】:

  • 正确而简洁的一个。但是你把它复制下来和我设计它的时间一样:)
  • 太棒了,那么ij 是什么?
  • 值得一提的是,这其实叫做Kadane的算法; 1984 年,卡内基梅隆大学的 Jay Kadane 发现(或发明)。它是唯一已知的解决此问题的线性时间算法,通常称为Maximum Subarray Problem
  • 当输入数组的所有值都是负数时不起作用
  • @jillesdewit ,随后的元素不一定是连续的。例如:在数组[1,2,3,4,5]中,[2,3,4]是子数组,[1,3,5]是子序列
【解决方案3】:

此 python 代码返回序列的边界。就原问题而言,i=bestloj=besthi-1

#
# given a sequence X of signed integers,
# find a contiguous subsequence that has maximal sum.
# return the lo and hi indices that bound the subsequence.
# the subsequence is X[lo:hi] (exclusive of hi).
#
def max_subseq(X):
    #
    # initialize vars to establish invariants.
    # 1: best subseq so far is [bestlo..besthi), and bestsum is its sum
    # 2: cur subseq is [curlo..curhi), and cursum is its sum
    #
    bestlo,besthi,bestsum  =  0,0,0
    curlo,curhi,cursum  =  0,0,0
    for i in xrange(len(X)):
        # extend current subseq and update vars
        curhi = i+1
        cursum += X[i]
        if cursum <= 0:
            #
            # the current subseq went under water,
            # so it can't be usefully extended.
            # start fresh at next index.
            #
            curlo = curhi
            cursum = 0
        elif cursum > bestsum:
            # adopt current subseq as the new best
            bestlo,besthi,bestsum  =  curlo,curhi,cursum

    return (bestlo,besthi)

以下是此代码通过的一些 doctest 示例。

    r'''
    doctest examples:
    >>> print max_subseq([])
    (0, 0)
    >>> print max_subseq([10])
    (0, 1)
    >>> print max_subseq([-1])
    (0, 0)
    >>> print max_subseq(xrange(5))
    (1, 5)
    >>> print max_subseq([-1, 1, -1])
    (1, 2)
    >>> print max_subseq([-1, -1, 1, 1, -1, -1, 1, 2, -1])
    (6, 8)
    >>> print max_subseq([-2, 11, -4, 13, -5, -2])
    (1, 4)
    >>> print max_subseq([4, -3, 5, -2, -1, 2, 6,-4])
    (0, 7)
    '''

【讨论】:

    【解决方案4】:

    你实际上需要 Kadane 的算法修改来记住子数组的上下界,这里是 C++11 代码:

    #include <iostream>
    #include <vector>
    
    typedef std::pair<std::vector<int>::iterator, std::vector<int>::iterator> SubSeq;
    
    SubSeq getMaxSubSeq(std::vector<int> &arr) {
        SubSeq maxSequence{arr.begin(), arr.begin()};
        auto tmpBegin = arr.begin();
        int maxEndingHere = 0;
        int maxSoFar = 0;
    
        for(auto it = arr.begin(); it < arr.end(); ++it) {
            int currentSum = maxEndingHere + *it;
    
            if(currentSum > 0) {
                if(maxEndingHere == 0) {
                    tmpBegin = it;
                }
                maxEndingHere = currentSum;
            } else {
                maxEndingHere = 0;
            }
    
            if(maxEndingHere > maxSoFar) {
                maxSoFar = maxEndingHere;
                maxSequence.first = tmpBegin;
                maxSequence.second = it + 1;
            }
        }
    
        return maxSequence;
    }
    
    int main()
    {
        std::vector<int> arr{-1, 2, 90, -50, 150, -300, 56, 12};
    
        auto seq = getMaxSubSeq(arr);
        while(seq.first != seq.second) {
            std::cout << *(seq.first) << " ";
            ++(seq.first);
        }
    
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-01-25
      • 2011-09-06
      • 2015-07-27
      • 2011-04-13
      • 1970-01-01
      • 1970-01-01
      • 2023-04-06
      相关资源
      最近更新 更多