【ACM】华为oj--合唱队

时间:2021-12-16 18:53:37
 

题目描述:

计算最少出列多少位同学,使得剩下的同学排成合唱队形

说明:

N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。 
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK,   则他们的身高满足存在i(1<=i<=K)使得Ti<T2<......<Ti-1<Ti>Ti+1>......>TK。 
     你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

输入:

整数N

一行整数,空格隔开,N位同学身高

输出:

最少需要几位同学出列

样例:

8 186 186 150 200 160 130 197 200

4

分析: 动态规划基础题。对每个身高求以它为终点的最长升子序列,以它为起点的最长降子序列,就可以求出需要出列的最少人数。实现的时候我是正序求一遍最长升子序列,再逆序求一遍最长升子序列。至于求最长子序列,简单解释一下:

将求以ak为终点的最长升子序列转化为:

MaxLen (k) = Max { MaxLen (i):1<i < k 且 ai < ak且 k≠1 } + 1

意思就是就是找出在ak左边,“终点”数值小于ak,且长度最大的那个上升子序列的长度再加1,就是ak的最长升子序列了。

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <stdlib.h>

#define MAX_N 1000

using namespace std;

int FindLeast(int arr[],int n)
{
int lissqe[MAX_N][2],i,j,minlen=n,maxlen;
lissqe[0][0]=1;
lissqe[n-1][1]=1;
for(i=1;i<n;i++)
{
maxlen=0;
for(j=i-1;j>=0;j--)
{
if(arr[j]<arr[i]&&lissqe[j][0]>maxlen)
{
maxlen=lissqe[j][0];
}

}
lissqe[i][0]=maxlen+1;
}
for(i=n-2;i>=0;i--)
{
maxlen=0;
for(j=i+1;j<n;j++)
{
if(arr[j]<arr[i]&&lissqe[j][1]>maxlen)
{
maxlen=lissqe[j][1];
}

}
lissqe[i][1]=maxlen+1;
}
for(i=0;i<n;i++)
{
if((n+1-lissqe[i][0]-lissqe[i][1])<minlen)
minlen=n+1-lissqe[i][0]-lissqe[i][1];
}
return minlen;
}

int main()
{
freopen("in.txt","r",stdin);
int N,sing[MAX_N];
cin>>N;
for(int i=0;i<N;i++)
{
cin>>sing[i];
}
cout<<FindLeast(sing,N)<<endl;
return 0;
}