跳到主要内容

HJ24. 合唱队

题目描述​

N 位同学站成一排,音乐老师要请其中的 (N-K) 位同学出列,使得剩下的 K 位同学排成合唱队形。合唱队形要求:从左到右先递增后递减,即存在一个"最高点"。

求最少需要出列多少人。

输入格式​

  • 第 1 行:整数 N(同学人数)
  • 第 2 行:N 个正整数,表示每个同学的身高

输出格式​

一个整数,表示最少需要出列的人数。

示例​

示例 1​

输入:

8
186 186 150 200 160 130 197 200

输出:

4

说明: 保留 186、200、197、200(最高点在 200 处,左侧 186<200、右侧 197<200),需出列 4 人。

示例 2​

输入:

6
2 1 5 3 4 2

输出:

2

解题思路​

第一步:理解问题本质​

合唱队形 = "先升后降",最高点 k 处要求:左侧 [0..k] 严格递增、右侧 [k..N-1] 严格递减。我们希望保留尽量多的人,等价于找到满足该条件的最长序列,再用总人数减它得到最少出列人数。

第二步:暴力解法​

枚举所有子序列,判断是否先升后降,取最长。2^N 种可能,指数级,不可行。

第三步:转为最长递增子序列(LIS)​

关键观察:对于每个位置 i 作为最高点,它能形成的合唱队形长度为:

  • 左侧从 0 到 i 的最长递增子序列长度(严格递增)
  • 右侧从 i 到 N-1 的最长递减子序列长度(即反向数组上的 LIS)

两者相加再减 1(位置 i 被算了两次),就是 i 作为最高点时可保留的最多人数。

第四步:最优解法(两次 LIS)​

用经典 O(N²) LIS 计算:

  • inc[i]:以位置 i 结尾的最长递增子序列长度
  • dec[i]:以位置 i 结尾的最长递增子序列长度(对反转数组求 LIS 后再反转)
inc = lis_len(heights)
dec = lis_len(heights[::-1])[::-1]
max_keep = max(inc[i] + dec[i] - 1 for i in range(N))
print(N - max_keep)

完整代码实现​

算法可视化 · 逐帧回放这道题的代码194 帧 · 录制输入
import sys


def lis_len(seq):
"""返回以每个位置结尾的最长递增子序列长度列表"""
n = len(seq)
dp = [1] * n
for i in range(n):
for j in range(i):
if seq[j] < seq[i]: # 严格递增
dp[i] = max(dp[i], dp[j] + 1)
return dp


def solve():
data = sys.stdin.buffer.read().split()
n = int(data[0])
heights = list(map(int, data[1:1 + n]))

inc = lis_len(heights)
dec = lis_len(heights[::-1])[::-1]

max_keep = max(inc[i] + dec[i] - 1 for i in range(n))
print(n - max_keep)


if __name__ == "__main__":
solve()

示例推演​

以示例 1 的 186 186 150 200 160 130 197 200 为例:

从左到右 LIS(inc):

位置身高inc
01861
11861
21501
32002
41602
51301
61973
72004

从右到左 LIS(dec):

位置身高dec
01863
11863
21502
32003
41602
51301
61971
72001

计算每个位置作为最高点时最多可保留的人数 inc[i] + dec[i] - 1:

位置incdec保留人数
0133
1133
2122
3234
4223
5111
6313
7414

结果:最多保留 max = 4 人,最少出列 = N - max = 8 - 4 = 4。


复杂度分析​

解法时间复杂度空间复杂度说明
暴力枚举O(2^N)O(N)指数级,不可行
两次 LISO(N²)O(N)N ≤ 3000,可接受

N 为同学人数


易错点总结​

1. 忘记"严格递增/递减"​

题目要求严格递增递减,seq[j] < seq[i] 不能写成 <=,否则相邻相等的身高会被错误保留。

2. dec 数组方向写错​

dec 是对反转数组求 LIS 后再反转,最容易写错。先反转、算完、再反转,保证 dec[i] 与 inc[i] 下标对齐。

3. 最高点重复计数​

inc[i] + dec[i] 把位置 i 数了两次,必须减 1。


扩展思考​

  • 本题 N 较小用 O(N²) 即可;若 N 达到 1e5,可用「贪心 + 二分」将 LIS 优化到 O(N log N)。
  • 同类"先升后降"问题都可以套用"正反两次 LIS"模型,例如求山峰数组的最长子序列。

相关题目​

自测​

加载评论中...