给定一个包含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
答案 0 :(得分:5)
让我们修复一个元素(a[i]
)。我们想要知道最右边元素的位置小于i
(L
)左侧的位置。我们还需要知道最左边元素的位置小于位于i
(R
)右侧的元素的位置。
如果我们知道L
和R
,我们应该在答案中添加(i - L) * (R - i) * a[i]
。
可以使用堆栈在线性时间内为所有L
预先计算R
和i
。伪代码:
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]
是一对<a[i], i>
。现在所有元素都是不同的。
时间复杂度为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
答案 1 :(得分:-1)
如果列表已排序,您可以将大小为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和sum(这里从零开始索引)。直觉是第一个元素是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)