题目解析最大相等分割红包金额 | 豆包MarsCode AI刷题

88 阅读4分钟

题目解析

小包收到了 NN 个甜点,每个甜点都有一个喜爱值。这些甜点被连续送来了 MM 次,并且这些甜点被首尾相接排成一排。现在小包希望从这 N×MN×M 个甜点中选择一段连续的甜点,使得这段甜点的总喜爱值最大化。

关键点:

  1. 甜点的排列:甜点被排列成 N×MN×M 个,即 NN 个甜点重复 MM 次。
  2. 连续子数组的最大和:我们需要找到一个连续子数组,使得其喜爱值之和最大。
  3. 至少选择一个甜点:即使所有甜点的喜爱值都是负数,小包也必须至少选择一个甜点。

解决方案推导

1. 单次送来的甜点

首先,我们需要计算单次送来的甜点中连续子数组的最大和。这可以通过经典的 Kadane 算法来实现。Kadane算法的核心思想是动态规划,通过遍历数组,维护两个变量:

def max_subarray_sum(arr):
    max_ending_here = max_so_far = arr[0]
    for x in arr[1:]:
        max_ending_here = max(x, max_ending_here + x)
        max_so_far = max(max_so_far, max_ending_here)
    return max_so_far
  • max_ending_here 表示以当前元素结尾的子数组的最大和。
  • max_so_far 表示全局的最大和。
  • 遍历数组,更新 max_ending_here 和 max_so_far

2. 多次送来的甜点

当 M>1M>1 时,甜点被重复排列。我们需要考虑以下几种情况:

  • 单次送来的甜点中的最大和:这是最简单的情况,直接使用 max_subarray_sum

  • 循环数组的最大和:由于甜点是重复排列的,我们需要考虑循环数组的情况。循环数组的最大和可以通过以下方法计算:

    • 计算数组的总和 total_sum
    • 计算数组中每个元素取反后的最大子数组和,这实际上是在寻找最小子数组和。
    • 循环数组的最大和为 total_sum + max_subarray_sum([-x for x in arr])
def max_circular_subarray_sum(arr):
    total_sum = sum(arr)
    max_subarray_sum_wrap = total_sum + max_subarray_sum([-x for x in arr])
    return max_subarray_sum_wrap
  • total_sum 是数组的总和。
  • max_subarray_sum([-x for x in arr]) 计算数组中每个元素取反后的最大子数组和。这个操作实际上是在寻找最小子数组和,因为取反后最小子数组和对应的是原数组中的最大子数组和。
  • max_subarray_sum_wrap 是总和加上取反后的最大子数组和,表示循环数组的最大子数组和。

3. 综合考虑

我们需要综合考虑单次送来的甜点中的最大和、循环数组的最大和,以及多次送来的甜点的总和。

  • 如果 total_sum > 0,说明甜点的总和是正的,可以考虑多次送来的甜点。此时,最大和可能是 max_single_segmentmax_circular_segment,或者是 total_sum * (M - 2) + max_circular_segment,即在中间部分重复甜点的总和加上两端的循环最大和。
  • 如果 total_sum <= 0,说明甜点的总和是负的或零,此时最大和只可能是 max_single_segment 或 max_circular_segment
def solution(N, M, data):
    max_single_segment = max_subarray_sum(data)
    
    if M == 1:
        return max_single_segment
    
    total_sum = sum(data)
    max_circular_segment = max_circular_subarray_sum(data)
    
    if total_sum > 0:
        return max(max_single_segment, max_circular_segment, total_sum * (M - 2) + max_circular_segment)
    else:
        return max(max_single_segment, max_circular_segment)
  • max_single_segment 是单次送来的甜点中连续子数组的最大和。
  • 如果 M==1M==1,直接返回 max_single_segment
  • total_sum 是单次送来的甜点的总和。
  • max_circular_segment 是循环数组的最大子数组和。
  • 如果 total_sum > 0,说明甜点的总和是正的,可以考虑多次送来的甜点。此时,最大和可能是 max_single_segmentmax_circular_segment,或者是 total_sum * (M - 2) + max_circular_segment,即在中间部分重复甜点的总和加上两端的循环最大和。
  • 如果 total_sum <= 0,说明甜点的总和是负的或零,此时最大和只可能是 max_single_segment 或 max_circular_segment

总结

代码通过动态规划和循环子数组的最大和来解决这个问题。它考虑了甜点排列的特殊性,并且根据甜点的总和是否为正来决定是否需要考虑多次送来的甜点。最终返回的是在 N×MN×M 个甜点中可以选择的连续甜点段的最大总喜爱值。