Files
python/TangDou/AcWing/LIS/482.md
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

163 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

##[$AcWing$ $482$. 合唱队形 ](https://www.acwing.com/problem/content/484/)
### 一、题目描述
$N$ 位同学站成一排,音乐老师要请其中的 $(N−K)$ 位同学出列,使得剩下的 $K$ 位同学排成合唱队形。     
合唱队形是指这样的一种队形:设 $K$ 位同学从左到右依次编号为 $1,2…,K$,他们的身高分别为 $T_1,T_2,…,T_K$,  则他们的身高满足 $T_1<…<T_i>T_i+1>…>T_K(1≤i≤K)$。     
你的任务是,已知所有 $N$ 位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
**输入格式**
输入的第一行是一个整数 $N$,表示同学的总数。
第二行有 $N$个整数,用空格分隔,第 $i$ 个整数 $T_i$ 是第 $i$位同学的身高(厘米)。
**输出格式**
输出包括一行,这一行只包含一个整数,就是最少需要几位同学出列。
**数据范围**
$2≤N≤100,130≤T_i≤230$
**输入样例**:
```cpp {.line-numbers}
8
186 186 150 200 160 130 197 220
```
**输出样例**:
```cpp {.line-numbers}
4
```
### 二、题意分析
就是和上一道的登山一样一样的,<font color='red' size=4><b>差异是:</b></font>,它问的是 **需要出列几个同学**!
#### $Code$
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int n; // 同学的个数
int a[N]; // 山的高度数组
int f[N]; // 从左向右的最长子序列
int g[N]; // 从右向左的最长子序列
int res;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
// 正向求解 LIS问题
for (int i = 1; i <= n; i++) {
f[i] = 1;
for (int j = 1; j < i; j++)
if (a[i] > a[j]) f[i] = max(f[i], f[j] + 1);
}
// 反向求解 LIS问题
for (int i = n; i >= 1; i--) {
g[i] = 1;
for (int j = n; j > i; j--)
if (a[i] > a[j]) g[i] = max(g[i], g[j] + 1);
}
// 因为最终的那个中间点,左边计算了一次,右边双计算了一次,需要减1去重复
for (int i = 1; i <= n; i++) res = max(res, f[i] + g[i] - 1);
// 问至少要出队几人,和问“mx=满足要求的队列,最长是多长”是直接相关的,如果算得mx,则n-mx就是答案
printf("%d\n", n - res);
return 0;
}
```
### 三、贪心+二分+记录路径$O(NlogN)$代码
#### $Code$
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int n, a[N];
int f[N], fl, p1[N];
int g[N], gl, p2[N];
int res;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
// 正向
f[++fl] = a[1];
p1[1] = 1;
for (int i = 2; i <= n; i++)
if (a[i] > f[fl]) {
f[++fl] = a[i];
p1[i] = fl;
} else {
int t = lower_bound(f + 1, f + fl + 1, a[i]) - f;
f[t] = a[i];
p1[i] = t;
}
// 反向
g[++gl] = a[n];
p2[n] = 1;
for (int i = n - 1; i >= 1; i--)
if (a[i] > g[gl]) {
g[++gl] = a[i];
p2[i] = gl;
} else {
int t = lower_bound(g + 1, g + gl + 1, a[i]) - g;
g[t] = a[i];
p2[i] = t;
}
for (int i = 1; i <= n; i++) res = max(res, p2[i] + p1[i] - 1);
printf("%d\n", n - res);
return 0;
}
```
### 四、状态机解法
#### $Code$
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int n;
int a[N];
int f[N][2];
int main() {
// input
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
// dp
for (int i = 1; i <= n; i++) {
f[i][0] = f[i][1] = 1;
for (int j = 1; j < i; j++) {
if (a[j] < a[i]) f[i][0] = max(f[i][0], f[j][0] + 1);
if (a[j] > a[i]) f[i][1] = max(f[i][1], max(f[j][0], f[j][1]) + 1);
}
}
// find result from all final states
int res = 0;
for (int i = 1; i <= n; i++) res = max({res, f[i][0], f[i][1]});
cout << n - res << endl;
return 0;
}
```