可以为我们提供最大“触发器”总和的子列表数组是啥?
Posted
技术标签:
【中文标题】可以为我们提供最大“触发器”总和的子列表数组是啥?【英文标题】:What is the sublist array that can give us maximum 'flip-flop' sum?可以为我们提供最大“触发器”总和的子列表数组是什么? 【发布时间】:2020-01-01 13:12:47 【问题描述】:我的问题是我得到了一个长度为 l 的数组。
假设这是我的数组:[1,5,4,2,9,3,6]
我们称之为A
。
这个数组可以有多个节点相邻的子数组。所以我们可以有[1,5,4]
或[2,9,3,6]
等等。每个子数组的长度无关紧要。
但诀窍在于求和部分。我们不能只添加所有数字,它就像触发器一样工作。所以对于子列表[2,9,3,6]
,总和将是[2,-9,3,-6]
,即:-10
。而且很小。
产生最大总和的数组 A 的子列表(或您喜欢的子数组)是什么?
一种可能的方法是(从直觉上)子列表[4,2,9]
将输出一个不错的结果:[4, -2, 9]
=(添加所有元素)=11
。
问题是,如何得出这样的结果? 给我们最大触发器和的子数组是什么?
主要是什么算法,以任意数组为输入,输出一个所有数相邻且总和最大的子数组?
我没有想出任何办法,但我很确定我应该选择动态编程或分而治之来解决这个问题。再说一遍,我不知道,我可能完全错了。
【问题讨论】:
【参考方案1】:这个问题确实可以使用动态规划来解决,方法是跟踪每个位置的最大总和。
但是,由于当前元素可以添加到总和中,也可以从总和中减去(取决于子序列的长度),我们将分别跟踪以此处结尾的最大总和,两者也一样作为奇数子序列长度。
下面的代码(在 python 中实现)就是这样做的(更多细节请参见代码中的 cmets)。 时间复杂度为 O(n)。
a = [1, 5, 4, 2, 9, 3, 6]
# initialize the best sequences which end at element a[0]
# best sequence with odd length ending at the current position
best_ending_here_odd = a[0] # the sequence sum value
best_ending_here_odd_start_idx = 0
# best sequence with even length ending at the current position
best_ending_here_even = 0 # the sequence sum value
best_ending_here_even_start_idx = 1
best_sum = 0
best_start_idx = 0
best_end_idx = 0
for i in range(1, len(a)):
# add/subtract the current element to the best sequences that
# ended in the previous element
best_ending_here_even, best_ending_here_odd = \
best_ending_here_odd - a[i], best_ending_here_even + a[i]
# swap starting positions (since a sequence which had odd length when it
# was ending at the previous element has even length now, and vice-versa)
best_ending_here_even_start_idx, best_ending_here_odd_start_idx = \
best_ending_here_odd_start_idx, best_ending_here_even_start_idx
# we can always make a sequence of even length with sum 0 (empty sequence)
if best_ending_here_even < 0:
best_ending_here_even = 0
best_ending_here_even_start_idx = i + 1
# update the best known sub-sequence if it is the case
if best_ending_here_even > best_sum:
best_sum = best_ending_here_even
best_start_idx = best_ending_here_even_start_idx
best_end_idx = i
if best_ending_here_odd > best_sum:
best_sum = best_ending_here_odd
best_start_idx = best_ending_here_odd_start_idx
best_end_idx = i
print(best_sum, best_start_idx, best_end_idx)
对于问题中的示例序列,上述代码输出如下触发器子序列:
4 - 2 + 9 - 3 + 6 = 14
【讨论】:
请您确定合并、分治步骤是什么?我们如何分析这个以及用什么方法来达到 O(n) 的复杂度? @Aliz:在这种动态方法中解决的子问题如下:“以元素 i 结尾的最佳子序列是什么,分别具有奇数和偶数长度” , 对于每个索引 i。这意味着我们正在解决 2 * n 个子问题。但是每个这样的子问题都在 O(1) 中通过使用前一个 i 的解决方案来解决。因此,时间复杂度为 O(n)。【参考方案2】:正如 quertyman 所写,我们可以使用动态编程。这类似于 Kadane 的算法,但有一些曲折。我们需要第二个临时变量来跟踪每个元素作为加法和减法的尝试。请注意,减法必须在加法之前,反之则不然。 O(1)
空间,O(n)
时间。
javascript 代码:
function f(A)
let prevAdd = [A[0], 1] // sum, length
let prevSubt = [0, 0]
let best = [0, -1, 0, null] // sum, idx, len, op
let add
let subt
for (let i=1; i<A.length; i++)
// Try adding
add = [A[i] + prevSubt[0], 1 + prevSubt[1]]
if (add[0] > best[0])
best = [add[0], i, add[1], ' + ']
// Try subtracting
if (prevAdd[0] - A[i] > 0)
subt = [prevAdd[0] - A[i], 1 + prevAdd[1]]
else
subt = [0, 0]
if (subt[0] > best[0])
best = [subt[0], i, subt[1], ' - ']
prevAdd = add
prevSubt = subt
return best
function show(A, sol)
let [sum, i, len, op] = sol
let str = A[i] + ' = ' + sum
for (let l=1; l<len; l++)
str = A[i-l] + op + str
op = op == ' + ' ? ' - ' : ' + '
return str
var A = [1, 5, 4, 2, 9, 3, 6]
console.log(JSON.stringify(A))
var sol = f(A)
console.log(JSON.stringify(sol))
console.log(show(A, sol))
更新
根据 OP 在 cmets 中的要求,这里有一些关于一般递归(伪代码)的理论阐述:让 f(i, subtract)
表示最大总和,包括在 i
处索引的元素,其中 subtract
表示是否或不是元素被减去或添加。那么:
// Try subtracting
f(i, true) =
if f(i-1, false) - A[i] > 0
then f(i-1, false) - A[i]
otherwise 0
// Try adding
f(i, false) =
A[i] + f(i-1, true)
(Note that when f(i-1, true) evaluates
to zero, the best ending at
i as an addition is just A[i])
循环只依赖于前一个元素的求值,这意味着我们可以用O(1)
空格对其进行编码,只需在每次迭代后保存最后的求值,并更新迄今为止最好的(包括序列的结束索引和长度,如果我们愿意)。
【讨论】:
你能想出一个伪代码或者把它转换成一个吗?由于我对算法和编程世界非常陌生,因此我很难理解这背后的逻辑。以上是关于可以为我们提供最大“触发器”总和的子列表数组是啥?的主要内容,如果未能解决你的问题,请参考以下文章
c_cpp 最大子阵列总和。在具有最大总和的数组(包含至少一个数字)中查找连续的子数组。
2021-07-16:三个无重叠子数组的最大和。给定数组 nums 由正整数组成,找到三个互不重叠的子数组的最大和。每个子数组的长度为k,我们要使这3*k个项的和最大化。返回每个区间起始索引的列表(索