使用分而治之的最小前缀数组
Posted
技术标签:
【中文标题】使用分而治之的最小前缀数组【英文标题】:Min-prefix-array using divide and conquer 【发布时间】:2021-11-27 21:50:52 【问题描述】:我正在努力解决一个分而治之的问题,因为我无法完全理解它。
假设我们有一些数组X[1:n]
。我们如何找到最小前缀数组X[1:k]
,其中1 ≤ k ≤ n
和前缀被定义为X[1]×X[2]×...×X[n]
的实数数组?
到目前为止,我的方法是:
function min_prefix(array[1:n],n)
begin
if array.length == 1 then
return n, array[0], array[0]
endif
integer best_k, real b_total, total = min_prefix([1:n-1],n-1)
new_total = total*array[n]
if new_total < b_total then
return n, new_total, new_total
endif
return best_k, b_total, new_total
end
我不认为这是一个有效的分而治之的解决方案,因为我仍然需要遍历数组中的每个元素。
编辑:
我能想到的最好的例子:
考虑数组-1,2,2,2
,最小前缀为k=3
,因为当所有元素相乘时,结果答案为-6。
但是,如果我们再考虑数组-1,2,-2,2
,那么最小前缀将是k=1
,因为k[0]*k[1] = -2
乘以第三个元素只会使数字更大。
【问题讨论】:
一定要“分而治之”吗?对于未排序的数组,我认为不可能在少于线性的时间内得到答案。 @Aivean 是的,它必须分而治之:/ @don'ttalkjustcode 修复了混淆并添加了示例 好,虽然你的例子有一些错误,例如k[0]
是双重错误,我认为应该是X[1]
。
而且“前缀产品”似乎是正确的术语。
【参考方案1】:
找到“最小前缀积”的算法基本上是计算所有可能的前缀并找到其中的最小值。这可以在线性时间内完成,而不是更快。
伪代码:
min_pref_l = 1
min_pref_v = arr[0]
prev_f = arr[0]
for i in 1 until arr.length:
pref_v *= arr[i]
if pref_v < min_pref_v:
min_pref_v = pref_v
min_pref_l = i + 1
return min_pref_v, min_pref_l
问题中最奇怪的部分是“分而治之”的要求。我想,如果你眯着眼睛看这个算法,你可能会说,它是“分而治之”,至于计算长度为i
的前缀,它使用了之前计算的长度为i-1
的前缀。
为了说明这一点,可以将算法重写为递归函数:
# min_prefix returns tuple of three values:
# First two define the minimal prefix of length ≤ i, as the pair of (value, length)
# Third is the product of the prefix of length i
fun min_prefix(i: int) -> (int, int, int):
if i == 0:
return arr[0], 1, arr[0]
prev_min_l, prev_min_v, prev_v = min_prefix(i-1)
v = prev_v * arr[i]
if v < prev_min_v:
return i+1, v, v
else:
return prev_min_l, prev_min_v, v
# program result
return min_prefix(arr.length - 1)
注意:
在递归变体中,空间复杂度从 O(1) 变为 O(n),函数可以重写为尾递归以避免这种情况 为了简化代码,没有刻意考虑诸如空数组和产品溢出等极端情况【讨论】:
以上是关于使用分而治之的最小前缀数组的主要内容,如果未能解决你的问题,请参考以下文章