一、题目解析
题目描述
我们需要找到一个数组中乘积最大的连续子区间,返回其起始和结束位置。特别的:
- 如果数组包含多个区间的最大乘积相同,则优先选择起始位置更小的区间。
- 如果起始位置也相同,则选择结束位置更小的区间。
思路梳理
-
动态规划思想:
- 在遍历数组的过程中,实时维护以当前位置为结尾的最大乘积和最小乘积。
- 因为负数的存在,最小乘积可能在乘以负数后转为最大乘积。
- 当遇到
0时,所有乘积重置为 1,区间重新开始。
-
贪心选择:
- 每次更新最大乘积时,记录对应的区间起止位置。
- 如果新找到的乘积与已记录的最大值相同,则根据题目要求选择区间。
代码详解
def solution(n: int, arr: list[int]) -> list[int]:
# 初始化变量,用于记录最大乘积及其对应的区间起点和终点
max_product = 0 # 最大乘积
start_idx = 0 # 最大乘积区间的起始位置
end_idx = 0 # 最大乘积区间的结束位置
current_max = 1 # 当前的最大乘积
current_min = 1 # 当前的最小乘积
temp_start = 0 # 临时记录当前区间的起点
for i in range(n):
if arr[i] == 0: # 遇到0时,重置乘积,重新开始
current_max = 1
current_min = 1
temp_start = i + 1 # 区间重新从下一位开始
continue
# 计算包括当前元素的最大乘积和最小乘积
temp_max = current_max * arr[i]
temp_min = current_min * arr[i]
current_max = max(arr[i], temp_max, temp_min)
current_min = min(arr[i], temp_max, temp_min)
# 更新最大乘积以及对应的区间
if current_max > max_product:
max_product = current_max
start_idx = temp_start
end_idx = i
elif current_max == max_product:
# 如果乘积相同,优先选择起点更小的区间
if temp_start < start_idx or (temp_start == start_idx and i < end_idx):
start_idx = temp_start
end_idx = i
return [start_idx + 1, end_idx + 1]
# 示例测试
print(solution(5, [1, 2, 4, 0, 8])) # 输出:[1, 3]
print(solution(7, [1, 2, 4, 8, 0, 256, 0])) # 输出:[6, 6]
print(solution(6, [0, 0, 1, 2, 4, 8])) # 输出:[3, 6]
二、知识总结
-
动态规划思想的灵活性:
- 本题通过动态更新以当前位置为终点的最大值和最小值,避免暴力枚举所有区间,大幅优化了时间复杂度。
-
处理特殊情况(如零值)的技巧:
- 遇到零值时,直接重置乘积,起点从下一位重新开始。
-
维护多个变量:
- 最大值和最小值的同步更新是动态规划中的常见模式,适用于包含负数或其他复杂规则的情况。
对其他入门同学的建议:
- 通过画图帮助理解最大值和最小值如何在数组中动态变化。
- 练习多个类似问题(如最大子数组和问题)以加深对动态规划的理解。
三、学习计划
-
制定刷题计划:
- 每天练习 2-3 道动态规划和滑动窗口类问题,循序渐进提升难度。
- 针对错题,重新审视解题思路,并对关键知识点进行总结。
-
利用错题进行针对性学习:
- 记录每道错题的错误原因(思路不清晰、实现有误等)。
- 重新手写代码并测试更多边界情况,确保完全理解。
四、工具运用
-
AI 刷题工具与知识点笔记结合:
- 在豆包MarsCode AI中搜索相关题目,获取解题提示和代码模板。
- 针对错题,利用AI生成更详细的解析和学习建议。
-
整合学习资源:
- 配合参考《算法导论》或其他算法书籍中的相关章节,对涉及的数学推导进行深入学习。
- 借助在线可视化工具(如动态规划可视化网站)直观理解算法流程。
五、总结
最大乘积区间问题不仅考验对动态规划的理解,还涉及对边界情况的处理和细节优化。通过刷题工具和学习资源的结合,持续练习和总结可以帮助快速提升编程能力。希望这份笔记对你的学习有所帮助!