原题描述
小R手上有一个长度为 n 的数组 (n > 0),数组中的元素分别来自集合 [0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]。小R想从这个数组中选取一段连续的区间,得到可能的最大乘积。
你需要帮助小R找到最大乘积的区间,并输出这个区间的起始位置 x 和结束位置 y (x ≤ y)。如果存在多个区间乘积相同的情况,优先选择 x 更小的区间;如果 x 相同,选择 y 更小的区间。
注意:数组的起始位置为 1,结束位置为 n。
解读
在这道题中,我们需要找到一个数组中连续区间,使其乘积最大。数组的元素来自于集合 ([0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]),并且可能包含 0。由于 0 会将乘积清零,我们可以把数组划分成多个不包含 0 的子数组分别处理。最终,我们要返回最大乘积的区间起始和结束位置。
解题思路
这道题的核心在于如何高效计算每个子数组的最大乘积。
- 分割子数组:遇到
0时,清除乘积并重新开始计算,将数组分成若干不含0的子数组。 - 双向遍历求最大乘积:对每个子数组,从左到右和从右到左各遍历一次,以防负数干扰。例如,当区间中包含负数时,乘积可能通过双向遍历的方式找到最大值。
- 维护区间位置:在所有子数组中选择乘积最大的区间,并按题目要求优先选择起始位置更小的区间。
具体解题过程
1:初始化与遍历
首先,我们遍历整个数组,跳过 0 并记录每个不包含 0 的子数组的起始和结束位置。使用以下代码完成:
max_product = 0
best_x, best_y = 1, 1
i = 0
while i < n:
if arr[i] == 0:
i += 1
continue
# 找到不包含0的子数组
start = i
while i < n and arr[i] != 0:
i += 1
end = i - 1
2:计算每个子数组的最大乘积
对每个不包含 0 的子数组,调用 find_max_product_in_subarray 函数进行双向遍历,找到最大乘积和对应的区间位置。代码如下:
def find_max_product_in_subarray(arr, start, end):
max_product = float('-inf')
current_product = 1
x, y = start, start
temp_start = start
# 从左到右遍历
for i in range(start, end + 1):
current_product *= arr[i]
if current_product > max_product:
max_product = current_product
x, y = temp_start, i
if current_product == 0: # 遇到0重置
current_product = 1
temp_start = i + 1
# 从右到左遍历
current_product = 1
temp_start = end
for i in range(end, start - 1, -1):
current_product *= arr[i]
if current_product > max_product:
max_product = current_product
x, y = i, temp_start
if current_product == 0: # 遇到0重置
current_product = 1
temp_start = i - 1
return max_product, x, y
3:更新全局最大乘积
在主函数中,我们将所有子数组的最大乘积进行比较,更新全局最大乘积并记录最佳区间位置:
max_subproduct, x, y = find_max_product_in_subarray(arr, start, end)
if max_subproduct > max_product:
max_product = max_subproduct
best_x, best_y = x + 1, y + 1 # 转换为从1开始的索引
elif max_subproduct == max_product:
if x + 1 < best_x or (x + 1 == best_x and y + 1 < best_y):
best_x, best_y = x + 1, y + 1
代码完整实现
完整代码:
def solution(n: int, arr: list[int]) -> list[int]:
def find_max_product_in_subarray(arr, start, end):
max_product = float('-inf')
current_product = 1
x, y = start, start
temp_start = start
# 从左到右遍历
for i in range(start, end + 1):
current_product *= arr[i]
if current_product > max_product:
max_product = current_product
x, y = temp_start, i
if current_product == 0:
current_product = 1
temp_start = i + 1
# 从右到左遍历
current_product = 1
temp_start = end
for i in range(end, start - 1, -1):
current_product *= arr[i]
if current_product > max_product:
max_product = current_product
x, y = i, temp_start
if current_product == 0:
current_product = 1
temp_start = i - 1
return max_product, x, y
max_product = 0
best_x, best_y = 1, 1
i = 0
while i < n:
if arr[i] == 0:
i += 1
continue
start = i
while i < n and arr[i] != 0:
i += 1
end = i - 1
max_subproduct, x, y = find_max_product_in_subarray(arr, start, end)
if max_subproduct > max_product:
max_product = max_subproduct
best_x, best_y = x + 1, y + 1
elif max_subproduct == max_product:
if x + 1 < best_x or (x + 1 == best_x and y + 1 < best_y):
best_x, best_y = x + 1, y + 1
return [best_x, best_y]
if __name__ == "__main__":
print(solution(5, [1, 2, 4, 0, 8]) == [1, 3])
print(solution(7, [1, 2, 4, 8, 0, 256, 0]) == [6, 6])
print(solution(8, [1, 2, 4, 8, 0, 256, 512, 0]) == [6, 7])
MarsCode提供的思路与提示
算法步骤
- 遍历数组:从左到右遍历数组,找到不包含0的子数组。
- 处理子数组:对于每个不包含0的子数组,计算其乘积,并记录最大乘积及其对应的区间。
- 考虑0的情况:如果遇到0,则重置当前乘积,并继续遍历。
- 比较和更新:在遍历过程中,不断比较当前乘积与最大乘积,更新最大乘积及其对应的区间。
具体步骤
- 初始化:初始化最大乘积为0,最佳区间为
[1, 1]。 - 遍历数组:从左到右遍历数组,跳过0。
- 找到不包含0的子数组:记录子数组的起始和结束位置。
- 计算子数组的乘积:对于每个子数组,计算其乘积,并记录最大乘积及其对应的区间。
- 更新最大乘积:如果当前子数组的乘积大于最大乘积,或者乘积相同但区间更优,则更新最大乘积及其对应的区间。
一点心得体会
在这道题中,通过双向遍历子数组来计算最大乘积是一个创新且有效的思路。这样的处理方式不仅避免了负数干扰,还保证了算法的高效性。使用双向扫描时,我们同时考虑了优先选择区间起始位置和结束位置更小的需求,使得代码在计算最大乘积的同时符合题目要求。
MarsCode AI 提供的双向遍历和分治策略,简化了负数干扰和 0 的处理,最终实现了最优解。