开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天,点击查看活动详情
P1091 [NOIP2004 提高组] 合唱队形 - 洛谷
题目描述
位同学站成一排,音乐老师要请其中的 位同学出列,使得剩下的 位同学排成合唱队形。
合唱队形是指这样的一种队形:设 位同学从左到右依次编号为 … ,他们的身高分别为 … ,则他们的身高满足 … 。
你的任务是,已知所有 位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
输入共两行。
第一行是一个整数 (),表示同学的总数。
第二行有 个整数,用空格分隔,第 个整数 ()是第 位同学的身高(厘米)。
输出一个整数,最少需要几位同学出列。
input
8
186 186 150 200 160 130 197 220
output
4
题目分析
这是一道最长上升子序列的问题。
由题目所述,被选择的 位同学的身高满足 … ,即排列中有一位最高点,整体呈现山字形排列。
因此,我们可以枚举 位同学中身高最高的同学的所处位置,设这位同学为顺最长上升子序列的右端点和逆最长上升子序列的左端点,即以其为最高点创建一个序列,将两个方向的最长子序列人数记录下来。
先正着跑一遍最长上升子序列,再逆着跑一遍最长上升子序列,最后依次遍历所有同学,求以其为最高点形成的排列中最多的人数,进而求得最优答案。
算法复杂度为 。
Accept代码(DP)
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int h[N], l[N], r[N];
int main()
{
int n; cin >> n;
for (int i = 0; i < n; i ++)
{
cin >> h[i];
l[i] = 1;
for (int j = 0; j < i; j ++)
{
if (h[i] > h[j]) l[i] = max(l[i], l[j] + 1);
}
}
for (int i = n - 1; ~i; i --)
{
r[i] = 1;
for (int j = n - 1; j > i; j --)
{
if (h[i] > h[j]) r[i] = max(r[i], r[j] + 1);
}
}
int res = n;
for (int i = 0; i < n; i ++) res = min(res, n - (l[i] + r[i] - 1));
cout << res;
return 0;
}