目录
描述
输入描述:
输出描述:
解题过程
提交代码
学习代码
代码一
代码二
收藏点
描述
N 位同学站成一排,音乐老师要请最少的同学出列,使得剩下的 K 位同学排成合唱队形。
设KK位同学从左到右依次编号为 1,2…,K ,他们的身高分别为T_1,T_2,…,T_KT1,T2,…,TK ,若存在i(1\leq i\leq K)i(1≤i≤K) 使得T_1<T_2<......<T_{i-1}<T_iT1<T2<......<Ti−1<Ti 且 T_i>T_{i+1}>......>T_KTi>Ti+1>......>TK,则称这KK名同学排成了合唱队形。
通俗来说,能找到一个同学,他的两边的同学身高都依次严格降低的队形就是合唱队形。
例子:
123 124 125 123 121 是一个合唱队形
123 123 124 122不是合唱队形,因为前两名同学身高相等,不符合要求
123 122 121 122不是合唱队形,因为找不到一个同学,他的两侧同学身高递减。
你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
注意:不允许改变队列元素的先后顺序 且 不要求最高同学左右人数必须相等
数据范围: 1≤n≤3000
输入描述:
用例两行数据,第一行是同学的总数 N ,第二行是 N 位同学的身高,以空格隔开
输出描述:
最少需要几位同学出列
解题过程
提交代码
学习代码
代码一
(作者:https://www.nowcoder.com/users/974547592)
动态规划,时间复杂度O(n^2)
#include <stdio.h>
#include <string.h>
int main(){
int N;scanf("%d",&N);
int a[N];int max;
for(int i=0;i<N;i++){
scanf("%d",&a[i]);
}
//逆序遍历,以当前元素为基准,记录其右边的最大严格递减序列的元素个数
int dp_r[N];memset(dp_r,0,sizeof(dp_r));//将这一块内存清零
//dp_r[x]指的是,在a[x]这个元素右边,比a[x]小的元素有几个
for(int i=N-2;i>-1;i--){//因为要记录的是右边最大严格递减序列的元素个数,所以i的初始值是N-2
max=-1;
for(int j=i+1;j<N;j++){
if(a[i]>a[j] && dp_r[j]>max){//max记录的是 最多的严格递减的个数
max=dp_r[j];
dp_r[i]=dp_r[j]+1;
}
}
}
//顺序遍历,以当前元素为基准,记录其左边的最大严格递增序列的元素个数
int dp_l[N];memset(dp_l,0,sizeof(dp_l));
for(int i=1;i<N;i++){
max=-1;
for(int j=i-1;j>-1;j--){
if(a[i]>a[j] && dp_l[j]>max){
max=dp_l[j];
dp_l[i]=dp_l[j]+1;
}
}
}
//遍历整个数组,计算当前元素dp_r[i]+dp_l[i]的大小,计算最大值
for(int i=0;i<N;i++){
if(dp_r[i]+dp_l[i]>max){
max=dp_r[i]+dp_l[i];
}
}
printf("%d",N-max-1);
}
代码二
(作者:https://www.nowcoder.com/users/974547592)
建立辅助数组,维持辅助数组元素有序排列,用二分查找找到原数组元素在该辅助数组中该插入的位置,该位置即为动态规划中dp[i]的值,时间复杂度O(nlogn)
#include <stdio.h>
#include <string.h>
int search(int* a,int key,int high){//二分查找
int low=0,mid;
while(low<=high){
mid=(low+high)/2;
if(a[mid]==key){
return mid;
}
else if(a[mid]>key){
high=mid-1;
}
else{
low=mid+1;
}
}
if(a[mid]>key){
return mid;
}
else{
return mid+1;
}
}
int main(){
int N;scanf("%d",&N);
int a[N];int b[N];memset(b,0,sizeof(b));//辅助数组
for(int i=0;i<N;i++){
scanf("%d",&a[i]);
}
int max=0;int p;
//顺序遍历,以当前元素为基准,记录其左边的最大严格递增序列的元素个数
int dp_l[N];dp_l[0]=0;
b[max]=b[0]=a[0];
for(int i=1;i<N;i++){
if(a[i]<=b[0]){//原数组元素值超过辅助数组下界就替换
dp_l[i]=0;
b[0]=a[i];
}
else if(a[i]>b[max]){//原数组元素值超过辅助数组上界,将其插入后形成新的上界
dp_l[i]=++max;
b[max]=a[i];
}
else{//原数组元素值在中间就在辅助数组中查找刚好大于它的数并替换
p=search(b,a[i],max);
dp_l[i]=p;
b[p]=a[i];
}
}
max=0;
//逆序遍历,以当前元素为基准,记录其右边的最大严格递减序列的元素个数
int dp_r[N];dp_r[N-1]=0;memset(b,0,sizeof(b));
b[max]=b[0]=a[N-1];
for(int i=N-2;i>-1;i--){
if(a[i]<=b[0]){//原数组元素值超过辅助数组下界就替换
dp_r[i]=0;
b[0]=a[i];
}
else if(a[i]>b[max]){//原数组元素值超过辅助数组上界,将其插入后形成新的上界
dp_r[i]=++max;
b[max]=a[i];
}
else{//原数组元素值在中间就在辅助数组中查找刚好大于它的数并替换
p=search(b,a[i],max);
dp_r[i]=p;
b[p]=a[i];
}
}
//遍历整个数组,计算当前元素dp_r[i]+dp_l[i]的大小,计算最大值
for(int i=0;i<N;i++){
if(dp_r[i]+dp_l[i]>max){
max=dp_r[i]+dp_l[i];
}
}
printf("%d",N-max-1);
}