首页 > 代码库 > [ACM] 九度OJ 合唱队形 (最长递增子序列改版)
[ACM] 九度OJ 合唱队形 (最长递增子序列改版)
时间限制:1 秒
内存限制:32 兆
特殊判题:否
提交:1680
解决:520
- 题目描述:
N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学不交换位置就能排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1, 2, …, K,他们的身高分别为T1, T2, …, TK,
则他们的身高满足T1 < T2 < … < Ti , Ti > Ti+1 > … > TK (1 <= i <= K)。
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
- 输入:
输入的第一行是一个整数N(2 <= N <= 100),表示同学的总数。
第一行有n个整数,用空格分隔,第i个整数Ti(130 <= Ti <= 230)是第i位同学的身高(厘米)。
- 输出:
可能包括多组测试数据,对于每组数据,
输出包括一行,这一行只包含一个整数,就是最少需要几位同学出列。
- 样例输入:
8186 186 150 200 160 130 197 220
- 样例输出:
4
- 来源:
- 2008年北京大学方正实验室计算机研究生机试真题
解题思路:
给N个数,从中剔除K个数,要求剩下数的序列成先上升后下降的状态。求最小的K。
对于N中的每一个数,都以它为“最高点”,求以它为结尾的最长递增子序列和以它为结尾的最长递减子序列的长度之和(注意该数被计算了两次,最后要减去1),那么总长度减去长度之和,就为K的大小。dpup[i]存的是以num[i]为结尾的最长递增子序列的长度,dpdown[i]存的是以num[i]为开头的最长递减子序列。枚举每个数就可以了,求出里面最大的长度之和,K减去它+1就是答案。这里计算最长递增子序列不用二分查找那种效率高的方法,而用dp[i]代表以num[i]为结尾的最长递增子序列的长度,因为它记录了其中每个数当结尾的最长递增子序列长度。求最长递减子序列也就是求尾到头求最长递增子序列。这样dpup[i]就是存的以num[i]为结尾的最长递增子序列的长度,dpdown[i]就是存的以num[i]为开头的最长递减子序列的长度。
代码:
#include <iostream>#include <stdio.h>using namespace std;int num[102];int dpup[102];//dpup[i]以num[i]为结尾的最长上升子序列int dpdown[102];//dpdown[i]以num[i]为开头的最长递减子序列int n;void LIS(int num[],int n)//最长上升子序列{ int m; dpup[1]=1; for(int i=2;i<=n;i++) { m=0; for(int j=1;j<i;j++) { if(dpup[j]>m&&num[j]<num[i]) m=dpup[j]; } dpup[i]=m+1; }}void LDS(int num[],int n)//最长递减子序列,逆序最长递增子序列{ int m; dpdown[n]=1; for(int i=n-1;i>=1;i--) { m=0; for(int j=n;j>i;j--) { if(dpdown[j]>m&&num[j]<num[i]) m=dpdown[j]; } dpdown[i]=m+1; }}int main(){ while(scanf("%d",&n)!=EOF) { for(int i=1;i<=n;i++) scanf("%d",&num[i]); LIS(num,n); LDS(num,n); int ans=0; for(int i=1;i<=n;i++) { if(ans<dpup[i]+dpdown[i]) ans=dpup[i]+dpdown[i];//ans为最长的先递增后递减的序列长度,但其中的一个数被算了两次,就是最“高”的那个数 } printf("%d\n",n-(ans-1)); } return 0;}