区间动态规划例题:合唱队形问题(最长递增递减子序列)
字数 1677 2025-11-06 22:52:24

区间动态规划例题:合唱队形问题(最长递增递减子序列)

题目描述:
有n个学生站成一排,每个学生有一个身高h[i]。现在需要从中选出一些学生组成合唱队形,要求队形满足:存在一个中间学生,其左边的所有学生身高严格递增,右边的所有学生身高严格递减。求最多能选出多少学生组成合唱队形。

解题过程:

  1. 问题分析:
  • 合唱队形实际上是一个"先递增后递减"的序列
  • 对于每个位置i,我们需要计算:
    • 以i结尾的最长递增子序列长度(从左往右)
    • 以i开头的最长递减子序列长度(从右往左)
  • 然后找到使两者之和最大的位置i
  1. 状态定义:
    定义两个数组:
  • left[i]:以第i个学生结尾的最长递增子序列长度
  • right[i]:以第i个学生开头的最长递减子序列长度
  1. 状态转移方程:

对于left数组(从左往右计算):
left[i] = max{left[j]} + 1,其中0 ≤ j < i 且 h[j] < h[i]
如果不存在这样的j,则left[i] = 1

对于right数组(从右往左计算):
right[i] = max{right[k]} + 1,其中i < k < n 且 h[i] > h[k]
如果不存在这样的k,则right[i] = 1

  1. 具体计算步骤:

步骤1:计算left数组

  • 初始化left[0] = 1
  • 对于i从1到n-1:
    • 设置left[i] = 1
    • 对于j从0到i-1:
      • 如果h[j] < h[i]且left[j] + 1 > left[i]:
        • left[i] = left[j] + 1

步骤2:计算right数组

  • 初始化right[n-1] = 1
  • 对于i从n-2到0:
    • 设置right[i] = 1
    • 对于k从i+1到n-1:
      • 如果h[i] > h[k]且right[k] + 1 > right[i]:
        • right[i] = right[k] + 1

步骤4:计算最大队形长度

  • max_len = 0
  • 对于每个位置i:
    • 队形长度 = left[i] + right[i] - 1(因为位置i被计算了两次)
    • max_len = max(max_len, 队形长度)
  1. 示例演示:
    假设身高序列为:[1, 3, 2, 4, 1]

计算left数组:

  • i=0: left[0]=1
  • i=1: 比较h[0]=1<h[1]=3 → left[1]=left[0]+1=2
  • i=2: 比较h[0]=1<h[2]=2 → left[2]=2
  • i=3: 比较前三个 → left[3]=max(left[0],left[1],left[2])+1=3
  • i=4: 无满足条件的j → left[4]=1
    left = [1, 2, 2, 3, 1]

计算right数组:

  • i=4: right[4]=1
  • i=3: 比较h[3]=4>h[4]=1 → right[3]=right[4]+1=2
  • i=2: 比较h[2]=2>h[4]=1 → right[2]=2
  • i=1: 比较h[1]=3>h[2]=2 → right[1]=right[2]+1=3
  • i=0: 比较h[0]=1<h[1]=3 → 不满足递减条件,right[0]=1
    right = [1, 3, 2, 2, 1]

计算每个位置的队形长度:

  • i=0: 1+1-1=1
  • i=1: 2+3-1=4
  • i=2: 2+2-1=3
  • i=3: 3+2-1=4
  • i=4: 1+1-1=1

最大队形长度为4,对应的队形可以是[1,3,2,1]或[1,3,4,1]

  1. 算法复杂度:
  • 时间复杂度:O(n²),需要两层循环
  • 空间复杂度:O(n),需要两个辅助数组
  1. 关键点:
  • 这个问题本质上是求每个位置作为"山峰"时的最长递增递减序列
  • 需要减去1是因为中间位置被重复计算了一次
  • 注意严格递增递减的条件(使用<和>而不是≤和≥)
区间动态规划例题:合唱队形问题(最长递增递减子序列) 题目描述: 有n个学生站成一排,每个学生有一个身高h[ i ]。现在需要从中选出一些学生组成合唱队形,要求队形满足:存在一个中间学生,其左边的所有学生身高严格递增,右边的所有学生身高严格递减。求最多能选出多少学生组成合唱队形。 解题过程: 问题分析: 合唱队形实际上是一个"先递增后递减"的序列 对于每个位置i,我们需要计算: 以i结尾的最长递增子序列长度(从左往右) 以i开头的最长递减子序列长度(从右往左) 然后找到使两者之和最大的位置i 状态定义: 定义两个数组: left[ i ]:以第i个学生结尾的最长递增子序列长度 right[ i ]:以第i个学生开头的最长递减子序列长度 状态转移方程: 对于left数组(从左往右计算): left[ i] = max{left[ j]} + 1,其中0 ≤ j < i 且 h[ j] < h[ i ] 如果不存在这样的j,则left[ i ] = 1 对于right数组(从右往左计算): right[ i] = max{right[ k]} + 1,其中i < k < n 且 h[ i] > h[ k ] 如果不存在这样的k,则right[ i ] = 1 具体计算步骤: 步骤1:计算left数组 初始化left[ 0 ] = 1 对于i从1到n-1: 设置left[ i ] = 1 对于j从0到i-1: 如果h[ j] < h[ i]且left[ j] + 1 > left[ i ]: left[ i] = left[ j ] + 1 步骤2:计算right数组 初始化right[ n-1 ] = 1 对于i从n-2到0: 设置right[ i ] = 1 对于k从i+1到n-1: 如果h[ i] > h[ k]且right[ k] + 1 > right[ i ]: right[ i] = right[ k ] + 1 步骤4:计算最大队形长度 max_ len = 0 对于每个位置i: 队形长度 = left[ i] + right[ i ] - 1(因为位置i被计算了两次) max_ len = max(max_ len, 队形长度) 示例演示: 假设身高序列为:[ 1, 3, 2, 4, 1 ] 计算left数组: i=0: left[ 0 ]=1 i=1: 比较h[ 0]=1<h[ 1]=3 → left[ 1]=left[ 0 ]+1=2 i=2: 比较h[ 0]=1<h[ 2]=2 → left[ 2 ]=2 i=3: 比较前三个 → left[ 3]=max(left[ 0],left[ 1],left[ 2 ])+1=3 i=4: 无满足条件的j → left[ 4 ]=1 left = [ 1, 2, 2, 3, 1 ] 计算right数组: i=4: right[ 4 ]=1 i=3: 比较h[ 3]=4>h[ 4]=1 → right[ 3]=right[ 4 ]+1=2 i=2: 比较h[ 2]=2>h[ 4]=1 → right[ 2 ]=2 i=1: 比较h[ 1]=3>h[ 2]=2 → right[ 1]=right[ 2 ]+1=3 i=0: 比较h[ 0]=1<h[ 1]=3 → 不满足递减条件,right[ 0 ]=1 right = [ 1, 3, 2, 2, 1 ] 计算每个位置的队形长度: i=0: 1+1-1=1 i=1: 2+3-1=4 i=2: 2+2-1=3 i=3: 3+2-1=4 i=4: 1+1-1=1 最大队形长度为4,对应的队形可以是[ 1,3,2,1]或[ 1,3,4,1 ] 算法复杂度: 时间复杂度:O(n²),需要两层循环 空间复杂度:O(n),需要两个辅助数组 关键点: 这个问题本质上是求每个位置作为"山峰"时的最长递增递减序列 需要减去1是因为中间位置被重复计算了一次 注意严格递增递减的条件(使用 <和>而不是≤和≥)