题目描述
N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2,…,K,他们的身高分别为T_1,T_2,…,T_K, 则他们的身高满足<…<>>…>。
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
输入格式
共二行。
第一行是一个整数,表示同学的总数。
第二行有n个整数,用空格分隔,第i个整数是第i位同学的身高(厘米)。
输出格式
一个整数,最少需要几位同学出列。
输入输出样例
输入 #1复制
8
186 186 150 200 160 130 197 220
输出 #1复制
4
说明/提示
对于50%的数据,保证有;
对于全部的数据,保证有。
思路:
其实思路就是挺好想的,因为数据规模不大,所以枚举以第 i 个数为最高点时的队列长度,然后选出最长的即可得到排除最少的人数。
而如何计算最长队列呢?很显然,只要算出 i 之前的最长升序子序列(并且末尾元素小于第 i 个元素)和 i 之后的最长降序子序列(并且首元素小于第 i 个元素)。
代码:
/*思路:转换思路 -> 求最长升序子序列和最长降序子序列 */
#include <cstdio>
#include <climits>
#include <algorithm>
using namespace std;
const int MAX = 110;
int N;
struct data
{
int d,l,r;
}datas[MAX];
int results[MAX];
void get_maxDecrease()
{
for(int i = N;i >= 1;i--){
datas[i].r = 1;
for(int j = N;j > i;j--)
if(datas[i].d > datas[j].d)
datas[i].r = max(datas[i].r,datas[j].r + 1);
}
}
//获取以site为结尾的最大长度
void get_maxIncrease()
{
for(int i = 1;i <= N;i++){
datas[i].l = 1;
for(int j = 1;j < i;j++)
if(datas[i].d > datas[j].d)
datas[i].l = max(datas[i].l,datas[j].l + 1);
}
}
void init()
{
get_maxIncrease();
get_maxDecrease();
return ;
}
int get_right(int i)
{
int myMax = 0;
for(int j = N;j > i;j--)
if(datas[j].d < datas[i].d&&myMax < datas[j].r)
myMax = datas[j].r;
return myMax;
}
int get_left(int i)
{
int myMax = 0;
for(int j = 1;j < i;j++)
if(datas[j].d < datas[i].d&&myMax < datas[j].l)
myMax = datas[j].l;
return myMax;
}
int get_ans()
{
int num = 0;
for(int i = 1;i <= N;i++){
int left = get_left(i);
int right = get_right(i);
left = i-1-left;
right = N-i-right;
num = left + right;
results[i] = num;
}
int myMin = INT_MAX;
for(int i = 1;i <= N;i++)
if(myMin > results[i])
myMin = results[i];
return myMin;
}
int main()
{
//读入数据并初始化:
scanf("%d",&N);
for(int i = 1;i <= N;i++)
scanf("%d",&datas[i].d);
init();
//处理:
int ans = get_ans();
//输出:
printf("%d",ans);
return 0;
}
总结:
这道题我一开始做错了,至于原因我也不陈述了(比较麻烦),但结论是,对于熟悉或者工具类的算法我们不要随意根据题更改,而要尽量像调用API一样使用它们。这样做有两个好处,一是不容易出错,二是久而久之这些常用代码(例如本题的最长子序列、二分查找之类的)会越来越熟悉。