题目描述
n 位同学站成一排,音乐老师要请其中的 n−k 位同学出列,使得剩下的 k 位同学排成合唱队形。
合唱队形是指这样的一种队形:设 kk 位同学从左到右依次编号为 1,2,…,k, 他们的身高分别为 t1,t2,…tk, 则他们的身高满足 t1<…t i+1>…>tk(1 <= i <= k)
你的任务是,已知所有 n 位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
输入格式
共二行。
第一行是一个整数 n(2≤n≤100),表示同学的总数。
第二行有 n 个整数,用空格分隔,第 i 个整数 ti (130 <= ti <= 230) 是第 i 位同学的身高(厘米)。
输出格式
一个整数,最少需要几位同学出列。
输入样例
8
186 186 150 200 160 130 197 220
输出样例
4
题目分析
枚举中间点 ti,分别求左边的最长上升子序列和右边的最长递减子序列。求右边的最长递减子序列的时候,可以转化成从右到左的最长上升子序列。把两边出队的人数相加就得到了全部的出队人数。
code
#include <iostream>
#include <cstring>
using namespace std;
const int MAX = 105;
const int INF = 0x3fffffff;
int a[MAX];
int dp[MAX];
int n;
int main()
{
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
a[0] = 0; //初始化,空段
a[n + 1] = 0;
int ans = INF;
//枚举中间点
for (int i = 1; i <= n; i++)
{
memset(dp, 0, sizeof(dp));
int cnt = 0;
//左边为上升序列
for (int j = 1; j <= i; j++)
for (int k = 0; k < j; k++)
if (a[k] < a[j])
dp[j] = max(dp[j], dp[k] + 1);
cnt += i - dp[i]; //左边出队的人数
//右边为下降序列
dp[i] = 0; //注意清零
for (int j = n; j >= i; j--)
for (int k = n + 1; k > j; k--)
if (a[k] < a[j])
dp[j] = max(dp[j], dp[k] + 1);
cnt += n - i + 1 - dp[i]; //右边出队的人数
ans = min(ans, cnt); //取最小值
}
cout << ans;
return 0;
}
- 无注释版
#include <iostream>
#include <cstring>
using namespace std;
const int MAX = 105;
const int INF = 0x3fffffff;
int a[MAX];
int dp[MAX];
int n;
int main()
{
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
a[0] = 0;
a[n + 1] = 0;
int ans = INF;
for (int i = 1; i <= n; i++)
{
memset(dp, 0, sizeof(dp));
int cnt = 0;
for (int j = 1; j <= i; j++)
for (int k = 0; k < j; k++)
if (a[k] < a[j])
dp[j] = max(dp[j], dp[k] + 1);
cnt += i - dp[i];
dp[i] = 0;
for (int j = n; j >= i; j--)
for (int k = n + 1; k > j; k--)
if (a[k] < a[j])
dp[j] = max(dp[j], dp[k] + 1);
cnt += n - i + 1 - dp[i];
ans = min(ans, cnt);
}
cout << ans;
return 0;
}