📌  相关文章
📜  最大子序列总和使得没有三个是连续的(1)

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

最大子序列总和使得没有三个是连续的

问题描述

给定一个整数数组,找到一个最大的子序列总和,使得在该子序列中不存在三个连续的数字。例如,给定数组 [3, 2, 5, 10, 7],则该最大子序列总和为 15,其中子序列为 [3, 5, 7]。注意,子序列中不能同时包含两个相邻的数字。

解决方案
动态规划

首先,我们可以对数组进行动态规划求解。设 $dp_i$ 为以第 $i$ 个数字结尾的最大的子序列总和,那么 $dp_i$ 与前面的状态有关。具体来说,$dp_i$ 可以由以下三种情况转移得到:

  1. 只包含第 $i$ 个数字:$dp_i = a_i$
  2. 包含第 $i$ 个数字和前面一个数字:$dp_i = a_i + a_{i-1}$
  3. 不包含第 $i$ 个数字,取前面的最大子序列总和:$dp_i = \max{dp_{i-1}, dp_{i-2}}$

但是,我们需要满足一个限制条件,即子序列中不存在三个连续的数字。为了满足这个限制条件,我们需要对转移方程进行修改。具体来说,当计算 $dp_i$ 时,我们需要考虑前两个状态,即 $dp_{i-1}$ 和 $dp_{i-2}$ 是否包含数字 $a_{i-1}$。如果两个状态都包含数字 $a_{i-1}$,那么 $dp_i$ 就应该等于 $dp_{i-3} + a_i$。因为如果 $dp_{i-1}$ 和 $dp_{i-2}$ 中都包含数字 $a_{i-1}$,那么子序列中就有了三个连续的数字,所以我们需要跳过数字 $a_{i-1}$。

最终的答案即为 $dp_i$ 中的最大值。

以下是 Python 代码实现:

def max_sum(nums):
    n = len(nums)
    if n == 0:
        return 0
    dp = [0] * n
    dp[0] = nums[0]
    if n > 1:
        dp[1] = nums[0] + nums[1]
    if n > 2:
        dp[2] = max(dp[1], nums[0] + nums[2], nums[1] + nums[2])
    for i in range(3, n):
        dp[i] = max(dp[i-1], dp[i-2] + nums[i], dp[i-3] + nums[i] + nums[i-1])
    return max(dp)
    
nums = [3, 2, 5, 10, 7]
print(max_sum(nums))  # 15
滚动数组

以上的动态规划算法使用了一个长度为 $n$ 的数组,空间复杂度为 $O(n)$。但是我们发现,每次计算 $dp_i$ 时,只需要用到 $dp_{i-1}$,$dp_{i-2}$ 和 $dp_{i-3}$ 这三个状态,因此可以使用滚动数组来优化空间复杂度,将空间复杂度优化到 $O(1)$。

以下是使用滚动数组的 Python 代码实现:

def max_sum(nums):
    n = len(nums)
    if n == 0:
        return 0
    dp = [0] * 3
    dp[0] = nums[0]
    if n > 1:
        dp[1] = nums[0] + nums[1]
    if n > 2:
        dp[2] = max(dp[1], nums[0] + nums[2], nums[1] + nums[2])
    for i in range(3, n):
        dp[0], dp[1], dp[2] = dp[1], dp[2], max(dp[2], dp[0] + nums[i], dp[1] + nums[i] + nums[i-1])
    return max(dp)
    
nums = [3, 2, 5, 10, 7]
print(max_sum(nums))  # 15
总结

这道题目是一个比较典型的动态规划问题,需要注意数字不能连续的限制条件,以及如何使用滚动数组优化空间复杂度。