vijos 1098 合唱队形 - 动态规划
描述
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个整数Ti(130<=Ti<=230)是第i位同学的身高(厘米)。
输出格式
输出包括一行,这一行只包含一个整数,就是最少需要几位同学出列。
限制
每个测试点1s
来源
NOIP 2004
(转自https://vijos.org/p/1098)
一看还真不好处理,至于高度从小到大再从大到小就先分开来看,先把从左边开始的不下降子序列求出来,不如用个l数组
l[i]表示从到i这个点最长的不下降子序列的长度,再用个r数组,表示从右边到i这个点最长不下降子序列的长度(注意是从右到左),然后再拼起来(l[i] + r[i]),减去一个人(第i个人算重了)就是i这个人当做k时最大可以选用的人的数量。
这样的看得话就简单多了,求2次最长不下降子序列,再for循环跑一趟就把最大可以选用的人数算出来。注意,l,r数组的初值是1(1个
人也可以排成这样的序列)
Code
1 /* 2 * vijos.org 3 * Problem#1098 4 * Accepted 5 * Time:45ms 6 * Memory:556k 7 */ 8 #include<iostream> 9 #include<cstdio> 10 #include<cctype> 11 #include<cstring> 12 using namespace std; 13 template<typename T> 14 inline void readInteger(T& u){ 15 char x; 16 while(!isdigit((x = getchar()))); 17 for(u = x - '0';isdigit((x = getchar()));u = ((u << 3) + (u << 1) + x - '0')); 18 ungetc(x, stdin); 19 } 20 int n; 21 int *l,*r; 22 int *h; 23 void init(){ 24 readInteger(n); 25 h = new int[(const int)(n + 1)]; 26 l = new int[(const int)(n + 1)]; 27 r = new int[(const int)(n + 1)]; 28 for(int i = 1;i <= n;i++){ 29 readInteger(h[i]); 30 } 31 } 32 int result; 33 void solve(){ 34 for(int i = 1;i <= n; i++){ 35 l[i] = r[n - i + 1] = 1; 36 for(int j = 1;j < i; j++){ 37 if(h[i] > h[j] && l[i] <= l[j]) 38 l[i] = l[j] + 1; 39 if(h[n - i + 1] > h[n - j + 1] && r[n - i + 1] <= r[n - j + 1]) 40 r[n - i + 1] = r[n - j + 1] + 1; 41 } 42 } 43 for(int i = 1;i <= n;i++) 44 if(l[i] + r[i] > result) result = l[i] + r[i]; 45 } 46 int main(){ 47 init(); 48 solve(); 49 printf("%d",n - result + 1); 50 return 0; 51 }