📜  最大化两个没有连续值的数组的子集总和(1)

📅  最后修改于: 2023-12-03 15:26:24.834000             🧑  作者: Mango

最大化两个没有连续值的数组的子集总和

题目描述

给定两个整数数组 nums1nums2,分别由 n1n2 个整数组成。找到每个数组中均没有相邻元素的子集,使得子集元素之和最大,并返回两个子集元素之和的最大和。

示例
示例1

输入:

nums1 = [3,2,5,10,7], nums2 = [8,4,2,5,6,10]

输出:

最大子集元素之和: 25

解释:

选取以下子集使它们之间没有连续值:

  • nums1: [3, 5, 7]
  • nums2: [8, 5, 10]

这些子集的元素之和分别为:15 + 23 = 25

解题思路
动态规划

题目要求不选择相邻元素的子集,因此我们需要排除元素相邻的情况。动态规划算法能够高效地解决本题。

  1. 定义状态

我们需要定义一个二维的状态数组 $dp$,其中 $dp[i][j]$ 表示选择 $i$ 个数,最后一个数在数组 $A$ 中第 $j$ 个位置时所能获得的最大子集元素之和。

  1. 状态转移

对于 $dp[i][j]$,我们需要考虑两种情况:

  • 当前位置 $j$ 选取: 如果我们选择 $A[j]$,那么我们需要在 $j-2$ 位置上选择 $i-1$ 个数,这时需要考虑 $dp[i-1][j-2]$。
  • 当前位置 $j$ 不选取: 如果我们不选择 $A[j]$,那么我们需要在 $j-1$ 位置上选择 $i$ 个数,这时需要考虑 $dp[i][j-1]$。

因此,状态转移方程如下:

$$ dp[i][j] = \max(dp[i][j-1], dp[i-1][j-2] + A[j]) $$

  1. 状态初始化

考虑到子集中至少需要选择一个数,我们需要将状态数组 $dp$ 的第一列进行初始化。由于 $dp[i][j]$ 表示选择 $i$ 个数,最后一个数在数组 $A$ 中第 $j$ 个位置时所能获得的最大子集元素之和,所以如果只选择一个数,那么最后一个数必须是 $A[j]$,也就是说 $dp[1][j]$ 的最大值就是 $A[j]$。

$$ dp[1][j] = A[j] $$

  1. 求解最优解

在求解最优解时,我们需要搜索整个状态数组 $dp$,找到最大的满足条件的状态值。

注意:我们还需要对数组 $B$ 进行一次动态规划,得到数组 $B$ 中任意不存在连续元素的子集的最大元素之和,记为 $max_b$。

状态转移方程如下:

$$ dp[i][j] = \max(max_a+max_b, dp[i][j-1], dp[i-1][j-2] + A[j]) $$

其中,$max_a$ 是数组 $A$ 中任意不存在连续元素的子集的最大元素之和。

最终的答案即为 $dp[n][m]$。

代码实现
def maxSum(nums1, nums2):
    def dp(nums):
        n = len(nums)
        dp = [0] * (n + 1)
        dp[1] = nums[0]
        for i in range(2, n + 1):
            dp[i] = max(dp[i - 1], dp[i - 2] + nums[i - 1])
        return dp[-1]

    max_a = max(dp(nums1), dp(nums2))
    m, n = len(nums1), len(nums2)
    dp = [[0] * (n + 1) for _ in range(m + 1)]
    for i in range(1, m + 1):
        for j in range(2, n + 1):
            dp[i][j] = max(max_a + dp[i - 1][j - 2] + nums2[j - 1],
                           dp[i][j - 1], dp[i - 1][j - 2] + nums1[i - 1])
    return dp[m][n]

以上是动态规划的实现,时间复杂度为 $O(n^2)$,空间复杂度为 $O(n^2)$。使用类似滚动数组的方法来优化空间复杂度,可以将空间复杂度缩小至 $O(n)$。

def maxSum(nums1, nums2):
    def dp(nums):
        n = len(nums)
        pre, cur = 0, nums[0]
        for i in range(2, n + 1):
            pre, cur = cur, max(cur, pre + nums[i - 1])
        return cur

    max_a = max(dp(nums1), dp(nums2))
    m, n = len(nums1), len(nums2)
    pre, cur = [0] * (n + 1), [0] * (n + 1)
    for i in range(1, m + 1):
        pre, cur = cur, [0] * (n + 1)
        for j in range(2, n + 1):
            cur[j] = max(max_a + pre[j - 2] + nums2[j - 1], cur[j - 1],
                         pre[j - 2] + nums1[i - 1])
    return cur[n]
总结

本题的关键在于动态规划的状态定义和状态转移方程的推导。最终的答案是两个状态数组的和,也就是两个子集的元素之和的最大值。时间复杂度为 $O(n^2)$,可以使用滚动数组优化空间复杂度至 $O(n)$。