首页 > 技术文章 > 合唱队

heyour 2020-03-19 17:15 原文

 

题目描述

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

说明:

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

请注意处理多组输入输出!

 

输入描述:

整数N

输出描述:

最少需要几位同学出列

示例1

输入

8
186 186 150 200 160 130 197 200

输出

4

#include <bits/stdc++.h>
using namespace std;
//这题采用的是动态规划的思想,但是我已开始没有思路,后来看了评论区的思路后,才重新写的
//然后,无意中容易犯一个错误:题目问的是需要去掉几个人,而动态规划求出的两个标记数组则是表示合唱团剩余的人数!!!
//从左向右或从右向左标记的代码值得铭记!!!别试图用O(n)的方式求解,至少目前没想到也没看到~
 int main(){
    int i, j, t, n;
    vector<int> vi, fgL, fgR;
    while(scanf("%d", &n) != EOF){
        vi.clear();
        fgL = vector<int>(n, 1); //初始状态
        fgR = vector<int>(n, 1);
        for(i=0; i<n; i++){
            scanf("%d", &t);
            vi.push_back(t);
        }
     //状态转移
for(i=1; i<n; i++){ //从左向右遍历 for(j=i-1; j>=0; j--){ if(vi[i]>vi[j] && fgL[j]>=fgL[i]) fgL[i] = fgL[j] + 1; } } for(i=n-2; i>=0; i--){ //从右向左遍历 for(j=i+1; j<n; j++){ if(vi[i]>vi[j] && fgR[j]>=fgR[i]) fgR[i] = fgR[j] + 1; } } t = 0; for(i=0; i<n; i++) if(fgL[i]+fgR[i] > t) t = fgL[i] + fgR[i]; printf("%d\n", n+1-t); //这里实际为n-(t-1) t-1的原因是第 i 个位置重复统计了! } return 0; }

总结:动态规划的想法,感觉不是很复杂,但是为什么每次都没有思路???也许练得还不够多吧!这个题挺经典的,之前碰见过类似的题目,基本特征是:从左向右遍历标记,再从右向左遍历标记,最后就得到了以每个位置出发的情况了!现在仔细想想,是不是每次标记前都是初始化为1呢?有点忘了,这个问题暂时存疑。

其次,因为是两轮标记,所以以每个位置出发的情况被重复统计了一次,所以要明白这点!

推荐阅读