问题描述
小U在公司年会上运气极佳,赢得了一等奖。作为一等奖得主,他有机会在一排红包中做两次切割,将红包分成三部分,要求第一部分和第三部分的红包总金额相等。他可以获得的金额是第一部分红包的总金额。帮小U计算出他能从这些红包中拿到的最大奖金金额。
输入是一个整数数组 redpacks,代表红包的金额。输出是小U能获得的最大金额。如果没有满足条件的分割,返回 0。
这道题的关键点是:
- 找到满足条件的切割点: 第一部分和第三部分的总金额相等。
- 计算最大收益: 从所有可能的切割方式中选择最大金额。
挑战点:
- 如何高效地找到切割点,而不是枚举所有可能的组合。
- 如何快速判断第一部分和第三部分的总金额是否相等。
代码逻辑
def solution(redpacks):
# 初始化指针和变量
p = 0 # 左指针,指向第一部分的末尾
q = len(redpacks) - 1 # 右指针,指向第三部分的起始
money = 0 # 保存当前满足条件的最大金额
left_sum = redpacks[p] # 第一部分的累计和
right_sum = redpacks[q] # 第三部分的累计和
# 双指针循环,直到两个指针相遇
while p < q:
if left_sum < right_sum:
# 如果第一部分的和小于第三部分的和,移动左指针
p += 1
left_sum += redpacks[p]
elif left_sum > right_sum:
# 如果第一部分的和大于第三部分的和,移动右指针
q -= 1
right_sum += redpacks[q]
if left_sum == right_sum:
# 如果第一部分和第三部分相等,更新最大金额
money = left_sum
# 继续尝试移动两端指针
p += 1
q -= 1
left_sum += redpacks[p]
right_sum += redpacks[q]
return money
算法设计
代码采用了 双指针 + 累计和 的方式解决问题,流程如下:
1. 双指针初始化
p指向数组的起始位置(左端),表示第一部分的右边界。q指向数组的末尾(右端),表示第三部分的左边界。left_sum累计第一部分的金额,初始值为redpacks[0]。right_sum累计第三部分的金额,初始值为redpacks[-1]。money用于记录满足条件时的最大金额,初始值为 0。
2. 移动指针,调整金额
-
当
left_sum < right_sum:第一部分的金额小于第三部分,说明需要扩大第一部分。移动左指针
p,并将其对应金额加入left_sum。 -
当
left_sum > right_sum:第一部分的金额大于第三部分,说明需要扩大第三部分。移动右指针
q,并将其对应金额加入right_sum。 -
更新后如果
left_sum == right_sum:满足条件时,更新
money = max(money, left_sum)。为了继续寻找更多可能的切割方案,尝试移动两端指针:
p += 1和q -= 1,并分别更新left_sum和right_sum。
3. 终止条件
- 当
p与q相遇或交错时,循环结束,返回当前记录的最大金额money。
测试分析
-
样例 1
输入:redpacks = [1, 3, 4, 6, 7, 14] 输出:14
- 第一次切割后第一部分为
[1, 3, 4, 6]和为 14,第三部分为[14]和为 14。
- 第一次切割后第一部分为
-
样例 2
输入:redpacks = [10000] 输出:0
- 无法分成三部分,返回 0。
-
样例 3
输入:redpacks = [10, 10, 10, 10] 输出:20
- 可切割成
[10, 10] | [10] | [10, 10],金额为 20。
- 可切割成
-
样例 4
输入:redpacks = [5, 5, 10, 20, 10, 5, 5] 输出:20
- 切割后第一部分和第三部分均为
[5, 5, 10],金额为 20。
- 切割后第一部分和第三部分均为
-
样例 5
输入:redpacks = [7, 7, 7, 21, 7, 7] 输出:14
- 切割后第一部分和第三部分均为
[7, 7],金额为 14。
- 切割后第一部分和第三部分均为
复杂度分析
-
时间复杂度:
- 双指针在数组上移动,每个元素最多访问一次,时间复杂度为 O(n)。
-
空间复杂度:
- 仅使用常数额外空间,空间复杂度为 O(1)。