【问题标题】:Sum of continuous sequences连续序列之和
【发布时间】:2015-02-28 08:44:43
【问题描述】:

给定一个包含 N 个元素的数组 A,我想在 A 的所有可能的连续子序列中找到最小元素的总和。我知道如果 N 很小,我们可以查找所有可能的子序列,但由于 N 最多10^5 找到这个总和的最佳方法是什么?

示例:设 N=3 和 A[1,2,3] 则 ans 为 10 作为可能的连续子序列 {(1),(2),(3),(1,2),(1,2 ,3),(2,3)} 所以最小元素之和 = 1 + 2 + 3 + 1 + 1 + 2 = 10

【问题讨论】:

  • 给定集合 A 的数字已排序或没有订单?
  • @L16H7 他们没有这样的命令
  • 你能给出 [1,2,2,3,5,5,5,6,6,7] 的预期结果吗?
  • @L16H7 {1}+min{1,2}+min{1,2,2}+min{1,2,2,3}+min{1,2,2, 3,5} 以此类推
  • 但是3和5不是连续的,为什么要考虑min{1,2,2,3,5}?

标签: algorithm


【解决方案1】:
  • 让我们修复一个元素(a[i])。我们想知道从i(L) 左侧比这个更小的最右边元素的位置。我们还需要知道从i(R) 中最左边的小于这个元素的位置。

  • 如果我们知道LR,我们应该在答案中加上(i - L) * (R - i) * a[i]

  • 可以使用堆栈在线性时间内预先计算所有iLR。伪代码:

    s = new Stack
    L = new int[n]
    fill(L, -1)
    for i <- 0 ... n - 1:
        while !s.isEmpty() && s.top().first > a[i]:
            s.pop()
        if !s.isEmpty():
            L[i] = s.top().second
        s.push(pair(a[i], i))
    

    我们可以反转数组并运行相同的算法来找到R

  • 如何处理相等的元素?假设a[i] 是一对&lt;a[i], i&gt;。现在所有元素都不同了。

时间复杂度为O(n)

这是一个完整的伪代码(我假设int 可以在这里保存任何整数值,你应该 选择可行的类型以避免实际代码中的溢出。我还假设所有元素都是不同的):

int[] getLeftSmallerElementPositions(int[] a):
    s = new Stack
    L = new int[n]
    fill(L, -1)
    for i <- 0 ... n - 1:
        while !s.isEmpty() && s.top().first > a[i]:
            s.pop()
        if !s.isEmpty():
            L[i] = s.top().second
        s.push(pair(a[i], i))
    return L

int[] getRightSmallerElementPositions(int[] a):
    R = getLeftSmallerElementPositions(reversed(a))
    for i <- 0 ... n - 1:
        R[i] = n - 1 - R[i]
    return reversed(R)

int findSum(int[] a):
    L = getLeftSmallerElementPositions(a)
    R = getRightSmallerElementPositions(a)
    int res = 0
    for i <- 0 ... n - 1:
        res += (i - L[i]) * (R[i] - i) * a[i]
    return res

【讨论】:

  • @kraskevich 你能添加整个伪代码吗?它使事情更清楚
  • 您假设基于 1 的索引?
  • @user3840069 我使用从 0 开始的索引。我还刚刚添加了一个完整的伪代码。
  • 它给出了 L[]={0,0,1} 和 R={0,1,2} 。这就是这两个数组的意思吗?
  • 我认为这个答案应该解释它为什么起作用。类似于:元素 i 是所有子字符串 a[l..r] 中的最小值,其中 L[i] 的频率
【解决方案2】:

如果列表已排序,您可以考虑大小为 1、2、3 到 N 的所有子集。该算法最初效率较低,但优化版本如下。这是一些伪代码。

let A = {1, 2, 3}
let total_sum = 0

for set_size <- 1 to N
    total_sum += sum(A[1:N-(set_size-1)])

首先,用一个元素设置:{{1}、{2}、{3}}:对每个元素求和。

然后,两个元素的集合 {{1, 2}, {2, 3}}:对除最后一个元素之外的每个元素求和。

然后,三个元素的集合 {{1, 2, 3}}:对除最后两个元素之外的每个元素求和。

但是这种算法效率低下。要优化到 O(n),请将每个第 i 个元素乘以 N-i 并求和(此处从零开始索引)。直觉是第一个元素是N个集合中的最小值,第二个元素是N-1个集合中的最小值,等等

我知道这不是 Python 问题,但有时代码会有所帮助:

A = [1, 2, 3]

# This is [3, 2, 1]
scale = range(len(A), 0, -1) 

# Take the element-wise product of the vectors, and sum
sum(a*b for (a,b) in zip(A, scale))

# Or just use the dot product
np.dot(A, scale)

【讨论】:

  • 他不是在找电源组,也不是 python 的问题。
  • @IVlad 更新以更准确地解决 OP 讨论的集合类型——我很抱歉。我已经用伪代码描述了我的算法的核心(但效率低下)版本,因为这不是 python 问题,然后口头描述了优化。希望你不介意最后的一点python。我真诚地相信它会对某人有所帮助。
猜你喜欢
  • 1970-01-01
  • 2022-01-25
  • 1970-01-01
  • 2016-05-03
  • 1970-01-01
  • 1970-01-01
  • 2011-06-29
  • 2015-08-23
  • 2023-03-27
相关资源
最近更新 更多