以每个位置为峰值,正向 LIS 得左段、反向 LIS 得右段,rise[i]+fall[i]-1 的最大值即最多保留人数,N 减之即为答案。
OJ: roj
题目 ID: 1264
难度:普及-
标签:DPLIS动态规划
创建: 2026-09-30 02:25
更新: 2026-10-05 07:28
题目描述
输入第一行为
样例输入:
text
8
186 186 150 200 160 130 197 220样例输出:
text
4思路
以每个位置 rise[i],右段等价于把序列反转后求一次同样的严格上升子序列 fall[i]。峰值被算了两次,所以最多保留人数为
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-30 02:25
# update_at: 2026-09-30 02:25
import sys
def lis_heights(nums: list[int]) -> list[int]:
"""以每个位置结尾、严格上升子序列的最大长度(LIS 经典 O(n^2) DP)。"""
dp: list[int] = [] # dp[i] = 以 nums[i] 结尾的 LIS 长度
for i, height in enumerate(nums):
dp.append(1 + max((dp[j] for j in range(i) if nums[j] < height), default=0))
return dp
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
n = next(data)
heights = [next(data) for _ in range(n)] # 每位同学的身高
rise = lis_heights(heights) # rise[i]:i 左侧(含 i)的最长严格上升
fall = lis_heights(heights[::-1])[::-1] # fall[i]:i 右侧(含 i)的最长严格下降
# 峰值算两次,减 1;n<=2 时队列必然不严格,max 兜底为 0,答案输出 n
keep = max((r + f - 1 for r, f in zip(rise, fall)), default=0)
print(n - max(keep, 0))
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-05 07:27
* update_at: 2026-10-05 07:27
*/
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 105;
ll h[MAXN]; // 每位同学的身高
ll rise[MAXN]; // rise[i]:以 i 结尾的最长严格上升子序列长度
ll fall[MAXN]; // fall[i]:从 i 开始的最长严格下降子序列长度
// 对数组 a[1..n] 求以每个位置结尾的最长严格上升子序列长度
void lis_strict(ll a[], ll dp[], ll n) {
for (ll i = 1; i <= n; i++) {
dp[i] = 1; // 至少包含自己
for (ll j = 1; j < i; j++) {
if (a[j] < a[i] && dp[j] + 1 > dp[i]) {
dp[i] = dp[j] + 1;
}
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
ll n;
cin >> n;
for (ll i = 1; i <= n; i++) {
cin >> h[i];
}
lis_strict(h, rise, n); // 正向求严格上升
// 反转序列求“严格上升”,对应原序列的“严格下降”
ll rev_h[MAXN];
ll rev_dp[MAXN];
for (ll i = 1; i <= n; i++) {
rev_h[i] = h[n - i + 1];
}
lis_strict(rev_h, rev_dp, n);
for (ll i = 1; i <= n; i++) {
fall[i] = rev_dp[n - i + 1];
}
ll keep = 0; // 最多能保留的同学数
for (ll i = 1; i <= n; i++) {
ll total = rise[i] + fall[i] - 1; // 峰值被算了两次,减 1
if (total > keep) {
keep = total;
}
}
cout << n - keep << "\n";
return 0;
}