青训营X豆包MarsCode 技术训练营刷题3 | 豆包MarsCode AI 刷题

57 阅读3分钟

数组分割与最大化总和问题

问题描述:

给定一个整数数组 arr,你可以将其划分为若干个连续的子数组,每个子数组的长度最多k。对于每个子数组,你可以将其中的所有元素替换为该子数组中的最大值。你的目标是找到一种划分方式,使得在对数组进行上述替换操作后,数组的总和最大化

任务是:

  • 找到一种最优的数组划分方式,使得经过替换操作后的数组元素之和达到最大。
  • 计算并输出这个最大和。

示例:

  • 示例 1:

    输入:arr = [1, 3, 7, 9, 5, 5, 8], k = 3

    输出:56

    解释:

    • 最优的划分方式是将数组分为 [1, 3, 7], [9], [5, 5, 8]
    • 替换后,每个子数组中的元素都变为该子数组的最大值,得到 [7, 7, 7], [9], [8, 8, 8]
    • 数组的总和为 7 + 7 + 7 + 9 + 8 + 8 + 8 = 56
  • 示例 2:

    输入:arr = [1, 4, 1, 5, 6], k = 2

    输出:24

  • 示例 3:

    输入:arr = [2, 3, 1, 2, 4], k = 2

    输出:16


注意:

  • 数组的长度可以是任何正整数。
  • 可以选择不分割数组(即将整个数组视为一个子数组),只要满足每个子数组的长度不超过 k
  • 需要考虑所有可能的划分方式,选择使总和最大的那一种。

解答

要解决这个问题,我们可以使用动态规划的方法。定义一个一维数组 dp,其中 dp[i] 表示前 i 个元素可以得到的最大和。对于每个位置 i,我们考虑长度不超过 k 的所有可能的子数组,并计算在将这些子数组中的元素替换为最大值后的总和。

算法步骤:

  1. 初始化 dp[0] = 0
  2. 对于每个位置 i1n
    • 初始化 curr_max = arr[i - 1]
    • 对于 j1min(k, i)
      • 更新 curr_max = max(curr_max, arr[i - j])
      • 更新 dp[i] = max(dp[i], dp[i - j] + curr_max * j)

代码实现(Python):

def maxSumAfterPartitioning(arr, k):
    n = len(arr)
    dp = [0] * (n + 1)
    for i in range(1, n + 1):
        curr_max = arr[i - 1]
        for j in range(1, min(k, i) + 1):
            curr_max = max(curr_max, arr[i - j])
            dp[i] = max(dp[i], dp[i - j] + curr_max * j)
    return dp[n]

# 测试样例
arr1 = [1, 3, 7, 9, 5, 5, 8]
k1 = 3
print(maxSumAfterPartitioning(arr1, k1))  # 输出:56

arr2 = [1, 4, 1, 5, 6]
k2 = 2
print(maxSumAfterPartitioning(arr2, k2))  # 输出:24

arr3 = [2, 3, 1, 2, 4]
k3 = 2
print(maxSumAfterPartitioning(arr3, k3))  # 输出:16

解释:

  • 样例1:

    • 最优分割方式是将数组分为 [1,3,7][9][5,5,8],对应的替换后子数组为 [7,7,7][9][8,8,8],总和为 7*3 + 9*1 + 8*3 = 56
  • 样例2:

    • 最优分割方式是将数组分为 [1,4][1,5][6],对应的替换后子数组为 [4,4][5,5][6],总和为 4*2 + 5*2 + 6*1 = 24
  • 样例3:

    • 最优分割方式是将数组分为 [2,3][1,2][4],对应的替换后子数组为 [3,3][2,2][4],总和为 3*2 + 2*2 + 4*1 = 16

时间复杂度分析:

  • 外层循环遍历数组,时间复杂度为 O(n)
  • 内层循环最多遍历 k 次,时间复杂度为 O(k)
  • 总时间复杂度为 O(n * k),在数据规模允许的情况下可以接受。