sql对数组元素切分_「Leetcode」1043.分隔数组以得到最大和

探讨如何将数组分区成最大小于等于K的连续子数组,然后将各子数组的值替换为其最大值,以实现数组总和最大化的动态规划算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

Given an integer array A, you partition the array into (contiguous) subarrays of length at most K. After partitioning, each subarray has their values changed to become the maximum value of that subarray.

Return the largest sum of the given array after partitioning.

Example

Input: A = [1,15,7,9,2,5,10], K = 3

Output: 84

Explanation: A becomes [15,15,15,9,10,10,10]

Note

  1. 1 <= K <= A.length <= 500
  2. 0 <= A[i] <= 10^6

分析

  1. 对数组A进行切分,切分后的每个子数组长度最大为K
  2. 每个子数组可以被其包含的元素最大值替换,例如[1,2,3]可以替换成[3,3,3]
  3. 找到能够使数组A所有元素和最大的处理方式,并返回该最大值
  4. 数组的长度区间为[1,500],元素值的范围是[0,10^6]

通过对于题目的分析可以发现,这是一道求最优解的题目,这种题目的解法不是贪心就是DP。再加上这道题的最大规模是500,所以基本上可以确定是O(n^2)的复杂度。

我们先来找一个简单的例子:A=[1,2,3], K=3

这个case只有4种切法:

  1. [1,2,3] -> [3,3,3] -> 9 -> answer
  2. [1,2] [3] -> [2,2][3] -> 7
  3. [1] [2,3] -> [1][3,3] -> 7
  4. [1][2][3] -> [1][2][3] -> 6

其实这里我已经把顺序按照从后向前切的方式排好了,[1,2,3]这个子数组独立出来之后,我们只关心当前这个子数组的状态和其之前的状态,即cur_sub_sum + pre_max_sum,这种只关心前面状态的方式就非常适合用dp来解了。

我们声明一个长度为n+1的数组dp,dp[i]表示当前第i个元素A数组所能达到的最大和,dp[0]被初始化为0,dp[n+1]即最终结果。

40aa0c66f697d523220f92d94eed95d9.png

我们还是以[1,2,3]来模拟一下整个过程:

当i=1时:

k = 1, m=1,此时dp[0]只能选择一个1

A: 1 2 3

dp: 0 1

当i=2时:

k=1时,m=2,此时dp[1]=max(dp[1], dp[0]+2*1)=2

A: 1 2 3

dp: 0 1 2

k=2时, m=2, 此时dp[1] = max(dp[1], d[0]+2*2) = 4

A: 1 2 3

dp: 0 1 4

当i=3时:

k=1时,m=3,此时dp[2]=max(dp[2], dp[0]+3*1)=3

A: 1 2 3

dp: 0 1 4 3

k=2时,m=3,此时dp[2]=max(dp[2], dp[0]+3*2)=6

A: 1 2 3

dp: 0 1 4 6

k=3时,m=3,此时dp[2]=max(dp[2], dp[0]+3*3)=9

A: 1 2 3

dp: 0 1 4 9

最终dp = [0,1,4,9]

在代码中可以看出子数组中的最大值m做了滚动处理,因此只需要维护一个变量即可。

### 关于 LeetCode 上使用分治法求解最大子数组和问题 #### 分治法的核心思想 分治法是一种通过将原问题分解成若干个小规模的相同子问题来解决的方法。对于最大子数组和问题,可以将其划分为三个部分:左半边的最大子数组和、右半边的最大子数组和以及跨越中间的最大子数组和[^3]。 --- #### 分治法的具体实现步骤 以下是基于分治法的思想设计的一个具体方案: 1. **定义递归函数** 定义一个辅助函数 `cross_sum(nums, left, mid, right)` 来计算跨越中间位置的最大子数组和;另一个函数 `max_subarray_sum(nums, left, right)` 则用于返回整个区间 `[left, right]` 的最大子数组和。 2. **划分左右两部分** 将当前数组分成两个子数组,分别位于索引范围 `[left, mid]` 和 `[mid+1, right]` 中,并递归调用上述函数处理这两部分。 3. **计算跨中点的最大子数组和** 计算从中间向两侧扩展所能得到的最大子数组和,这一步骤的时间复杂度为 \(O(n)\),因为每次只需遍历一次左侧和右侧即可找到最优解。 4. **合并结果** 返回三者中的最大值作为最终的结果:即左边区间的最大子数组和、右边区间的最大子数组和以及跨越中间的最大子数组和。 --- #### Python 实现代码 下面是完整的 Python 代码实现: ```python def maxSubArray(nums): def cross_sum(nums, left, mid, right): """计算跨越中间的最大子数组和""" if left == right: # 如果只有一个元素,则直接返回该元素 return nums[left] # 向左扩展找最大前缀和 left_max = float('-inf') total = 0 for i in range(mid, left - 1, -1): # 注意是从 mid 开始往左走 total += nums[i] if total > left_max: left_max = total # 向右扩展找最大后缀和 right_max = float('-inf') total = 0 for i in range(mid + 1, right + 1): # 注意是从 mid+1 开始往右走 total += nums[i] if total > right_max: right_max = total return left_max + right_max def max_subarray_sum(nums, left, right): """递归计算最大子数组和""" if left == right: # 基本情况:单个元素 return nums[left] mid = (left + right) // 2 # 左侧最大子数组和 left_sum = max_subarray_sum(nums, left, mid) # 右侧最大子数组和 right_sum = max_subarray_sum(nums, mid + 1, right) # 跨越中间的最大子数组和 cross_sum_value = cross_sum(nums, left, mid, right) return max(left_sum, right_sum, cross_sum_value) return max_subarray_sum(nums, 0, len(nums) - 1) ``` --- #### 示例分析 考虑输入 `nums = [-2,1,-3,4,-1,2,1,-5,4]`: - 当我们递归到某个阶段时,假设当前分割点为 `mid=3`(对应数值为 `4`),则: - 左侧最大子数组和为 `-2 + 1 - 3 = -4`; - 右侧最大子数组和为 `4 - 1 + 2 + 1 = 6`; - 跨越中间的最大子数组和为 `(1) + (4 - 1 + 2 + 1) = 7`。 - 最终取三者的最大值,因此答案为 `7`。 注意,在实际运行过程中,由于进一步细分,最终得出全局最大值仍为 `6`[^5]。 --- #### 时间与空间复杂度分析 - **时间复杂度**: 每次都将数组一分为二并线性扫描一遍,总共有 \(\log n\) 层递归树,每层操作量为 \(n\),故总体时间为 \(O(n\log n)\)。 - **空间复杂度**: 主要由递归栈决定,最坏情况下深度为 \(\log n\),所以空间复杂度为 \(O(\log n)\)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值