【问题标题】:optimal way to find sum(S) of all contiguous sub-array's max difference找到所有连续子阵列最大差的总和(S)的最佳方法
【发布时间】:2015-08-22 06:54:17
【问题描述】:

给定一个包含 n 个元素的数组:d[0], d[1], ..., d[n-1]。 计算所有连续子数组最大差的总和(S)。

形式上:S = sum{max{d[l,...,r]} - min{d[l, ..., r}},∀ 0

输入:

4 
1 3 2 4

输出:

12

说明:

l = 0; r = 0;数组:[1] sum = max([1]) - min([1]) = 0

l = 0; r = 1;数组:[1,3] sum = max([1,3]) - min([1,3]) = 3 - 1 = 2

l = 0; r = 2;数组:[1,3,2] sum = max([1,3,2]) - min([1,3,2]) = 3 - 1 = 2

l = 0;r = 3;数组:[1,3,2,4] sum = max([1,3,2,4]) - min([1,3,2,4]) = 4 - 1 = 3

l = 1; r = 1 将导致零

l = 1; r = 2;数组:[3,2] sum = max([3,2]) - min([3,2]) = 3 - 2 = 1;

l = 1; r = 3;数组:[3,2,4] sum = max ([3,2,4]) - min([3,2,4]) = 4 - 2 = 2;

l = 2; r = 2;将导致零

l = 2; r = 3;数组:[2,4] sum = max([2,4]) - min([2,4]) = 4 -2 = 2;

l = 3; r = 3 将导致零;

总和 = 12

我的想法: 暴力检查所有可能的子集;传染性阵列。

How to optimize it for larger number?

【问题讨论】:

    标签: arrays algorithm optimization subset


    【解决方案1】:

    这可以在线性时间内完成!每个元素对于它是最大值的每个子数组都会进入一次总和,并且对于每个它是最小值的子数组,每个元素都会减去一次。我们需要一个线性时间算法来找出每个元素的最大值或最小值有多少个子数组,我们可以通过对 all nearest smaller values 算法的微小修改来做到这一点。

    我们的想法是,为了找出一个元素的最大子数组数量,我们保留一堆我们看到的大于我们看到的所有后续元素的元素,以及这些数字的位置。当我们找到一个大于堆栈中最后一个元素的元素时,我们知道子数组可以延伸到堆栈顶部元素的左侧或右侧多远,并且仍然保持最大值,我们可以使用它来确定它的最大值是多少子数组。我们可以通过简单地否定数组的所有元素来处理最小值。

    def max_sums(d):
        stack = [(-1, float('inf'))]
        sum_ = 0
        for i, x in enumerate(d):
            while x > stack[-1][1]:
                prev_i, prev_x = stack.pop()
                prev_prev_i, prev_prev_x = stack[-1]
                sum_ += prev_x * (i - prev_i) * (prev_i - prev_prev_i)
            stack.append((i, x))
        while len(stack) > 1:
            prev_i, prev_x = stack.pop()
            prev_prev_i, prev_prev_x = stack[-1]
            sum_ += prev_x * (len(d) - prev_i) * (prev_i - prev_prev_i)
        return sum_
    
    def max_differences_sum(d):
        return max_sums(d) + max_sums([-x for x in d])
    

    这是算法的一个示例运行。假设输入是[30, 10, 40, 20]。然后为了计算所有子数组的最大值之和,我们对输入进行迭代,如下所示:

    30
    

    我们将(0, 30) 压入堆栈。堆栈现在记录我们在索引 0 处看到了 30。

    10
    

    30 > 10,所以我们将(1, 10) 压入堆栈。堆栈现在记录我们在索引 1 处看到了 10。

    40
    

    10 < 40,因此最大 10 的子数组不能包含此元素。我们看到一个最大值为 10 的子数组必须在索引 30 之后开始并在索引 40 之前结束,所以它有 1 个可能的左端点和 1 个可能的右端点,并且有1*1 这样的数组。我们将10*1*1 添加到总和中并从堆栈中弹出(1, 10)。现在总和是 10。

    30 < 40,因此最大 30 的子数组也不能包含该元素。我们看到一个最大为 30 的子数组必须从索引 0 开始并在索引 0 或索引 1 处结束,所以有1*2 这样的数组。我们将30*1*2 添加到总和并弹出(0, 30)。现在总和是 70。

    堆栈现在是空的,所以我们推送(2, 40)

    20
    

    40 > 20,所以我们推送(3, 20)

    我们已经遍历了所有输入,因此对于仍在数组上的任何对 (i, x),最大为 x 的子数组可以从索引 i 到数组末尾的任意位置结束,并且它可以从任意位置开始从i 到前一个堆栈条目的索引(如果没有前一个条目,则为数组的开头)。

    (3, 20) 在堆栈上,其下方有(2, 40),因此最大为20 的子数组必须在索引3 处开始和结束。我们将20*1*1 添加到总和并弹出(3, 20)。现在总和是 90。

    (2, 40) 在堆栈上,下面没有任何内容,因此最大 40 的子数组可以从任何索引 = 2 结束。我们将 40*3*2 添加到总和并为空堆栈。现在总和是 330。

    我们已经考虑了总和中的所有正项。为了减去最小值,我们将所有输入元素取反并再次通过上述算法输入它们。我们最终减去 170,总共为 160。

    【讨论】:

    • 您没有考虑子数组的最小值。我们需要找到每个子数组的 max-min 之间的差异。而且代码真的很难理解。如果您评论它您正在尝试做的事情并提供您的算法工作的示例,那将会很好。谢谢
    • @Cyclotron3x3:它确实考虑了最小值;这就是max_sums([-x for x in d]) 所做的。
    • 很抱歉给您带来更多困扰,您能否提供一个算法工作示例。很难理解你的 python 代码。 (我是 java :))...谢谢
    • @Cyclotron3x3:我添加了一个示例运行。
    • @user2357112 :感谢您的贡献。如果您可以看一下,我可能在此解决方案中遇到错误(stackoverflow.com/a/40949437/1222674)。谢谢!
    【解决方案2】:

    假设您有一个长度为 n 的序列,并且您希望计算某个固定大小 m 的滑动窗口的最小值(或最大值)。然后(令人惊讶地)this can be done in O(n) time

    所以现在对于窗口大小m = 1, ..., n,你需要从左到右运行滑动窗口;对于窗口的每张幻灯片,您只需要添加窗口内元素的最大值 - 最小值。由上可知,运行时间为Theta(n^2)。这改进了您的简单算法,即 Theta(n^3)

    【讨论】:

    • 他可以在 O(1) 中找到最小值和最大值。当nr 相等时,设置 min = max = arr[l],然后在添加下一项时,将其与 min 和 max 进行比较,并根据需要更改它们
    • @splash58 他能在 O(1) 中找到最小值和最大值吗?滑动窗口的所有特定大小的运行?整个问题?随意编写替代解决方案。我不确定 cmets 是否可以解释这一点。
    • 我已经写过不需要计算最大值和最小值。我认为这会减少时间估计
    • @AmiTavory 是的,滑动窗口将是一个很好的方法。适合这个问题。
    • @splash58 我明白你的意思,但它仍然不会是 O(1) 。它肯定会提高性能。
    猜你喜欢
    • 2016-05-09
    • 2018-05-03
    • 1970-01-01
    • 2015-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-29
    相关资源
    最近更新 更多