最长湍流子数组问题(进阶版)
字数 1787 2025-11-26 07:27:45

最长湍流子数组问题(进阶版)

我将为您讲解一个进阶版本的最长湍流子数组问题。这个问题在基础版本上增加了额外的约束条件,使得解题思路更加复杂和有趣。

题目描述

给定一个整数数组 arr,当数组的子数组满足以下条件时,我们称该子数组为湍流子数组:

  • 对于每个索引 i(子数组的起始索引 < i < 子数组的结束索引),比较符号在相邻元素之间交替变化:
    • 如果 arr[i-1] < arr[i],那么 arr[i] > arr[i+1]
    • 如果 arr[i-1] > arr[i],那么 arr[i] < arr[i+1]

进阶约束:现在允许在最多修改一个元素的值的情况下,找出最长的湍流子数组的长度。修改一个元素意味着可以将其改为任意整数值。

解题思路

这个问题可以分为两个部分来思考:

  1. 首先理解基础版本(不允许修改)的湍流子数组问题
  2. 然后考虑如何扩展到允许修改一个元素的情况

基础版本解法回顾

在基础版本中,我们使用动态规划:

  • 定义 up[i]:以位置 i 结尾,且 arr[i] > arr[i-1] 的最长湍流子数组长度
  • 定义 down[i]:以位置 i 结尾,且 arr[i] < arr[i-1] 的最长湍流子数组长度

状态转移方程:

  • 如果 arr[i] > arr[i-1]up[i] = down[i-1] + 1down[i] = 1
  • 如果 arr[i] < arr[i-1]down[i] = up[i-1] + 1up[i] = 1
  • 如果 arr[i] == arr[i-1]up[i] = down[i] = 1

进阶版本解法

对于进阶版本,我们需要考虑修改一个元素的情况。我们可以使用三维动态规划:

状态定义

定义 dp[i][state][modified],其中:

  • i:当前考虑的位置
  • state:当前的状态(0表示上升,1表示下降)
  • modified:是否已经使用过修改机会(0表示未使用,1表示已使用)

状态转移方程

对于每个位置 i,考虑所有可能的情况:

情况1:没有使用修改机会

  1. 如果 arr[i] > arr[i-1] 且前一个状态是下降:
    dp[i][0][0] = dp[i-1][1][0] + 1
  2. 如果 arr[i] < arr[i-1] 且前一个状态是上升:
    dp[i][1][0] = dp[i-1][0][0] + 1
  3. 如果不满足湍流条件,重新开始计数

情况2:使用修改机会
这里的关键是我们可以修改 arr[i] 来满足湍流条件:

  1. 如果前一个状态是下降,我们可以修改 arr[i] 使其大于 arr[i-1]
    dp[i][0][1] = max(dp[i][0][1], dp[i-1][1][0] + 1)

  2. 如果前一个状态是上升,我们可以修改 arr[i] 使其小于 arr[i-1]
    dp[i][1][1] = max(dp[i][1][1], dp[i-1][0][0] + 1)

  3. 我们也可以选择不在当前位置使用修改机会,而是继承之前已经使用过修改机会的状态

初始化

对于第一个元素:

  • dp[0][0][0] = 1(上升状态)
  • dp[0][1][0] = 1(下降状态)
  • dp[0][0][1] = 1
  • dp[0][1][1] = 1

算法步骤

  1. 初始化DP数组
  2. 遍历数组中的每个元素
  3. 对于每个元素,考虑所有可能的状态转移
  4. 记录过程中的最大值
  5. 返回最大值作为结果

代码实现

def maxTurbulenceSize(arr):
    if not arr:
        return 0
    
    n = len(arr)
    if n == 1:
        return 1
    
    # dp[i][state][modified]
    # state: 0-up, 1-down
    # modified: 0-no, 1-yes
    dp = [[[0] * 2 for _ in range(2)] for _ in range(n)]
    
    # 初始化
    dp[0][0][0] = 1
    dp[0][1][0] = 1
    dp[0][0][1] = 1
    dp[0][1][1] = 1
    
    max_len = 1
    
    for i in range(1, n):
        # 情况1:不使用修改机会
        if arr[i] > arr[i-1]:
            # 当前上升,前一个应该是下降
            dp[i][0][0] = dp[i-1][1][0] + 1
            dp[i][1][0] = 1
        elif arr[i] < arr[i-1]:
            # 当前下降,前一个应该是上升
            dp[i][1][0] = dp[i-1][0][0] + 1
            dp[i][0][0] = 1
        else:
            # 相等,重新开始
            dp[i][0][0] = 1
            dp[i][1][0] = 1
        
        # 情况2:使用修改机会
        # 选择1:在当前位置使用修改机会
        # 修改arr[i]使其满足湍流条件
        if i >= 1:
            # 修改arr[i]使其大于arr[i-1](当前状态为上升)
            dp[i][0][1] = max(dp[i][0][1], dp[i-1][1][0] + 1)
            # 修改arr[i]使其小于arr[i-1](当前状态为下降)
            dp[i][1][1] = max(dp[i][1][1], dp[i-1][0][0] + 1)
        
        # 选择2:不在当前位置使用修改机会,继承之前的状态
        if arr[i] > arr[i-1]:
            dp[i][0][1] = max(dp[i][0][1], dp[i-1][1][1] + 1)
        elif arr[i] < arr[i-1]:
            dp[i][1][1] = max(dp[i][1][1], dp[i-1][0][1] + 1)
        else:
            # 如果相等,我们可以选择修改其中一个来继续序列
            dp[i][0][1] = max(dp[i][0][1], dp[i-1][1][1] + 1)
            dp[i][1][1] = max(dp[i][1][1], dp[i-1][0][1] + 1)
        
        # 更新最大值
        max_len = max(max_len, dp[i][0][0], dp[i][1][0], dp[i][0][1], dp[i][1][1])
    
    return max_len

复杂度分析

  • 时间复杂度:O(n),其中n是数组的长度。我们只需要遍历数组一次。
  • 空间复杂度:O(n),用于存储DP数组。可以通过滚动数组优化到O(1)。

示例演示

考虑数组 [9,4,2,10,7,8,8,1,9]

基础版本(不允许修改):
最长的湍流子数组是 [4,2,10,7,8,1,9],长度为7。

进阶版本(允许修改一次):
我们可以修改第二个8(索引6)为其他值,得到更长的湍流子数组 [2,10,7,8,1,9] 或其他更优序列。

这个问题的关键在于理解修改机会的使用时机,以及如何通过动态规划来跟踪不同的状态。通过这种方法,我们能够有效地解决这个进阶版本的湍流子数组问题。

最长湍流子数组问题(进阶版) 我将为您讲解一个进阶版本的最长湍流子数组问题。这个问题在基础版本上增加了额外的约束条件,使得解题思路更加复杂和有趣。 题目描述 给定一个整数数组 arr ,当数组的子数组满足以下条件时,我们称该子数组为湍流子数组: 对于每个索引 i (子数组的起始索引 < i < 子数组的结束索引),比较符号在相邻元素之间交替变化: 如果 arr[i-1] < arr[i] ,那么 arr[i] > arr[i+1] 如果 arr[i-1] > arr[i] ,那么 arr[i] < arr[i+1] 进阶约束 :现在允许在最多修改一个元素的值的情况下,找出最长的湍流子数组的长度。修改一个元素意味着可以将其改为任意整数值。 解题思路 这个问题可以分为两个部分来思考: 首先理解基础版本(不允许修改)的湍流子数组问题 然后考虑如何扩展到允许修改一个元素的情况 基础版本解法回顾 在基础版本中,我们使用动态规划: 定义 up[i] :以位置 i 结尾,且 arr[i] > arr[i-1] 的最长湍流子数组长度 定义 down[i] :以位置 i 结尾,且 arr[i] < arr[i-1] 的最长湍流子数组长度 状态转移方程: 如果 arr[i] > arr[i-1] : up[i] = down[i-1] + 1 , down[i] = 1 如果 arr[i] < arr[i-1] : down[i] = up[i-1] + 1 , up[i] = 1 如果 arr[i] == arr[i-1] : up[i] = down[i] = 1 进阶版本解法 对于进阶版本,我们需要考虑修改一个元素的情况。我们可以使用三维动态规划: 状态定义 定义 dp[i][state][modified] ,其中: i :当前考虑的位置 state :当前的状态(0表示上升,1表示下降) modified :是否已经使用过修改机会(0表示未使用,1表示已使用) 状态转移方程 对于每个位置 i ,考虑所有可能的情况: 情况1:没有使用修改机会 如果 arr[i] > arr[i-1] 且前一个状态是下降: dp[i][0][0] = dp[i-1][1][0] + 1 如果 arr[i] < arr[i-1] 且前一个状态是上升: dp[i][1][0] = dp[i-1][0][0] + 1 如果不满足湍流条件,重新开始计数 情况2:使用修改机会 这里的关键是我们可以修改 arr[i] 来满足湍流条件: 如果前一个状态是下降,我们可以修改 arr[i] 使其大于 arr[i-1] : dp[i][0][1] = max(dp[i][0][1], dp[i-1][1][0] + 1) 如果前一个状态是上升,我们可以修改 arr[i] 使其小于 arr[i-1] : dp[i][1][1] = max(dp[i][1][1], dp[i-1][0][0] + 1) 我们也可以选择不在当前位置使用修改机会,而是继承之前已经使用过修改机会的状态 初始化 对于第一个元素: dp[0][0][0] = 1 (上升状态) dp[0][1][0] = 1 (下降状态) dp[0][0][1] = 1 dp[0][1][1] = 1 算法步骤 初始化DP数组 遍历数组中的每个元素 对于每个元素,考虑所有可能的状态转移 记录过程中的最大值 返回最大值作为结果 代码实现 复杂度分析 时间复杂度 :O(n),其中n是数组的长度。我们只需要遍历数组一次。 空间复杂度 :O(n),用于存储DP数组。可以通过滚动数组优化到O(1)。 示例演示 考虑数组 [9,4,2,10,7,8,8,1,9] : 基础版本 (不允许修改): 最长的湍流子数组是 [4,2,10,7,8,1,9] ,长度为7。 进阶版本 (允许修改一次): 我们可以修改第二个8(索引6)为其他值,得到更长的湍流子数组 [2,10,7,8,1,9] 或其他更优序列。 这个问题的关键在于理解修改机会的使用时机,以及如何通过动态规划来跟踪不同的状态。通过这种方法,我们能够有效地解决这个进阶版本的湍流子数组问题。