最大相等分割红包金额题解
问题描述
小U在公司年会上赢得了一等奖,作为奖励,他有机会在一排红包中做两次切割,将红包分成三部分。要求第一部分和第三部分的红包总金额相等。他可以获得的金额是第一部分红包的总金额。现在的问题是,给定红包的金额列表,计算出小U能从这些红包中拿到的最大奖金金额。
思路解析
这个问题可以通过前缀和(prefix sum)来有效求解。我们需要进行两次切割,且要求切割后第一部分和第三部分的金额相等。通过前缀和可以快速计算任意区间的和,从而简化问题的求解过程。
具体思路:
-
前缀和:首先我们可以计算出一个前缀和数组
prefix_sum,其中prefix_sum[i]表示从第一个红包到第i个红包的总和。这样,通过prefix_sum数组可以快速得到任何子数组的和。 -
找切割点:我们需要在红包列表中选择两个切割点,分别将红包分成三部分。我们假设切割点j是第二部分的结束位置。
-
条件判断:为了满足题目要求,第一部分和第三部分的总金额必须相等,如果满足条件,我们可以更新最大奖金金额。
解题代码
def solution(redpacks):
n = len(redpacks)
if n < 3:
return 0
prefix_sum = [0] * (n + 1)
for i in range(n):
prefix_sum[i + 1] = prefix_sum[i] + redpacks[i]
max_amount = 0
# 遍历
for j in range(2, n):
seen_sums = set()
for i in range(1, j + 1):
if prefix_sum[i] == prefix_sum[n] - prefix_sum[j]:
seen_sums.add(prefix_sum[i])
max_amount = max(max_amount, prefix_sum[i])
return max_amount
if __name__ == "__main__":
# Add your test cases here
print(solution([1, 3, 4, 6, 7, 14]) == 14)
print(solution([10000]) == 0)
print(solution([52, 13, 61, 64, 42, 26, 4, 27, 25]) == 52)
解题思路详解
-
前缀和的计算: 首先,我们创建一个
prefix_sum数组,其中prefix_sum[i]表示从第一个红包到第i个红包的总和。计算方法是,prefix_sum[i]等于prefix_sum[i-1]加上当前红包的金额。 -
遍历切割位置:
- 我们从第二个位置开始考虑切割点j,因为切割的第二部分必须包含至少一个红包。
- 对于每一个切割点j,我们遍历从1到
j-1的所有可能的切割位置i,检查是否满足条件:prefix_sum[i] == prefix_sum[n] - prefix_sum[j]。如果满足条件,则表示第一部分和第三部分的总金额相等。
-
更新最大奖金: 当满足条件时,我们将
prefix_sum[i]更新为当前的最大奖金金额,并继续寻找其他可能的组合。 -
返回结果: 最终返回最大奖金金额,如果没有找到合法的组合,则返回0。
复杂度分析
-
时间复杂度:
计算前缀和需要
O(n)时间。对于每个j,我们遍历所有i(i小于j),最坏情况下是O(n^2)。因此,总的时间复杂度为O(n^2),其中n是红包的数量。 -
空间复杂度:
我们使用了一个长度为
n+1的数组prefix_sum来存储前缀和,空间复杂度是O(n)。 另外,seen_sums是一个集合,用来记录每次遇到的前缀和,空间复杂度也是O(n)。 因此,空间复杂度是O(n)。
感受与注意事项
-
边界情况: 如果红包的数量小于3个,就无法进行有效的切割,因为无法将红包分成三部分。在这种情况下,我们直接返回0。
-
前缀和的高效性: 使用前缀和数组使得我们能够在常数时间内计算任意子数组的和,从而有效地简化了问题的复杂度。没有使用前缀和的情况下,我们需要重复计算多个子数组的和,这会导致效率低下。
-
理解切割点的选择: 切割点i和j的选择是关键。切割点i应该在1到
j-1之间,确保第一部分和第三部分都包含至少一个红包。切割点j应该从2开始,确保第三部分不为空。