算法刷题——贪心的小包 | 豆包MarsCode AI刷题

122 阅读3分钟

贪心的小包

以豆包MarsCode AI 刷题(代码练习)题库中贪心的小包一题为例

题目分析

通过阅读题目可以知道:由于甜点的排列是循环的,可以理解为一个长度为 N * M 的数组。我们要找到这段数组中的连续子数组,使其总和最大化

那么可以将所给的数组重复 M 次进行拼接,这样我们的问题就变成了:在拼接完后的数组中寻找数组中的连续子数组,使其总和最大化

假设拼接后的数组为 all ,那么最简单的思路便是:遍历 all 数组,并依次计算从当前位置开始到数组末尾的累加过程中出现的最大值,并保存下来,返回即可

具体的代码实现:

def solution(N, M, data):
    # 拼接所有送来的甜点
    all = []
    for _ in range(M):
        all.extend(data)
    # 甜点总数
    l = len(all)

    # 存储最大值
    max_num = all[-1]

    # 遍历数组
    for i in range(l):
        result = count_after_max(all, i)
        if result > max_num:
            max_num = result

    # 返回最大值
    return max_num
    
# 计算从i开始的最大值
def count_after_max(arr, i):
    themax = arr[i]
    count = 0
    for i in arr[i:]:
        count += i
        if count > themax:
            themax = count
    return themax

思路优化

这样虽然可以成功计算出我们想要的结果,但是分析一下计算过程就会发现,有大量重复性的计算,拼接后的数组 all 里,越往后的元素会被反复加上很多次,我们必须想方法进行优化

观察拼接后数组的情况,既然有大量循环的结构,由于是连续的,那么意味着实际上只需要考虑前两次 data 数组的重复即可,即在 data * 2 上找到最大连续子数组和,并不需要重复拼接 M

  • 这样可以避免直接构造一个过长的数组,提高效率
  • 但此时我们需要考虑重复的次数带来的额外价值

具体的代码实现:

def solution(N, M, data):  
    # 数据数组的总和
    sum_data = sum(data)
    
    # 单个数据数组的最大子数组和
    max_single = kadane(data)
    
    # 数据重复两次的最大子数组和(覆盖循环情况)
    max_double = kadane(data * 2)
    
    # 如果sum_data是正数,那么可以多次使用完整的数据数组
    if M == 1:
        max_result = max_single
    elif sum_data > 0:
        max_result = max_double + (M - 2) * sum_data
    else:
        max_result = max_double
    
    return max_result

# Kadane 算法
def kadane(arr):
    # 初始化最大和为数组的第一个元素
    max_sum = arr[0]
    # 初始化当前和为数组的第一个元素
    current_sum = arr[0]
    # 遍历数组的其余元素
    for i in range(1, len(arr)):
        # 更新当前和为当前元素和当前和加当前元素中的较大值
        current_sum = max(arr[i], current_sum + arr[i])
        # 更新最大和为最大和和当前和中的较大值
        max_sum = max(max_sum, current_sum)
    # 返回最大和
    return max_sum

最后 max_result 返回值的确定计算的解释:

  • 如果 M == 1,即数组只重复一次,那么最大结果就是单个数据数组的最大子数组和
  • 如果 sum_data > 0,即数组的总和大于 0,那么最大结果是数据重复两次的最大子数组和加上 (M - 2) 个数据数组的总和
  • 否则,最大结果是数据重复两次的最大子数组和

复杂度分析

  • 时间复杂度: O(N) 使用 Kadane 算法对 datadata * 2 进行遍历
  • 空间复杂度: O(1),不需要额外的空间

这是一道经典的循环数组的最大子数组和问题。本题难度并不算很高,如果在一开始没有思路,也可以使用豆包MarsCode的AI刷题功能,让AI给出一点思路提示和引导,通过多次反复刷题以识别这类题型,从而提高自己的算法能力