题目描述
N位同学站成一排,音乐老师要请其中的(N−K)位同学出列,使得剩下的K位同学排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2,…,K,他们的身高分别为T1,T2,…,TK, 则他们的身高满足T1<…<Ti>Ti+1>…>Tk(1≤i≤k)。
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
输入格式
共二行。
第一行是一个整数N(2≤N≤100),表示同学的总数。
第二行有n个整数,用空格分隔,第i个整数i(130≤T**i≤230)是第i位同学的身高(厘米)。
输出格式
一个整数,最少需要几位同学出列。
输入输出样例
输入 #1
8
186 186 150 200 160 130 197 220
输出 #1
4
说明/提示
对于50%的数据,保证有n≤20;
对于全部的数据,保证有n≤100。
题解:
首先分析这道题的取值范围,范围非常的小,n <= 100,
由于取值范围非常的小,这道题有两种解法:1.贪心+二分.2.DP(动态规划)
这里写出动态规划解法:
这是一道线性DP问题,非常简单,求解的是一个上升子序列问题
(题目中的下降子序列可以用相同方法,逆序求出)
我们状态表示:表示为以i结尾的上升子序列的集合;属性是这一段上升子序列集合中元素数目的最大值.
状态转移方程经过分析为 f[i] = max(f[i], f[j] +1) j为开始到j中间的任意一个元素下标.
C++
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N = 110;
int h[N];
int f[N], g[N];
int n;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &h[i]);
for (int i = 1; i <= n; i++) {
f[i] = 1;
for (int j = 1; j < i; j++)
if (h[j] < h[i])
f[i] = max(f[i], f[j] + 1);
}
for (int i = n; i; i--) {
g[i] = 1;
for (int j = n; j > i; j--)
if (h[j] < h[i])
g[i] = max(g[i], g[j] + 1);
}
int res = 0;
for (int i = 1; i <= n; i++)
res = max(res, f[i] + g[i] - 1);
printf("%d\n", n - res);
return 0;
}
java
import java.util.Scanner;
public class Main {
static final int N = 110;
static int h[] = new int[N];
static int f[] = new int[N];
static int g[] = new int[N];
static int n;
public static void main(String [] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for(int i = 1; i <= n; i++)
h[i] = sc.nextInt();
sc.close();
for (int i = 1; i <= n; i++) {
f[i] = 1;
for (int j = 1; j < i; j++)
if (h[j] < h[i])
f[i] = Math.max(f[i], f[j] + 1);
}
for (int i = n; i != 0; i--) {
g[i] = 1;
for (int j = n; j > i; j--)
if (h[i] > h[j])
g[i] = Math.max(g[i], g[j] + 1);
}
int res = 0;
for (int i = 1; i <= n; i++)
res = Math.max(res, f[i] + g[i] - 1);
System.out.println(n - res);
}
}