动态规划

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

代码示例:

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Main {
    public static void main(String[] args) throws IOException {
        //Scanner in = new Scanner(new FileInputStream("D:\JavaData\tmp/input.txt"));
        //Scanner in = new Scanner(System.in);
        //BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream("D:\JavaData\tmp/input.txt")));
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        String input = null;
        while ((input=in.readLine())!=null) {
            Integer nums = Integer.parseInt(input);           //数据个数
            input = in.readLine();
            String[] inputStrs = input.split(" ");
            Integer[] inputNum = new Integer[inputStrs.length]; //输入的数据
            Integer[] incPosNum = new Integer[inputNum.length];  //保存计算的每个数在最大递增子串中的位置
            Integer[] descPosNum = new Integer[inputNum.length];//保存计算的每个数在最大递减子串中的位置
            for(int i=0;i<inputStrs.length;i++){
                inputNum[i] = Integer.parseInt(inputStrs[i]);
                //设置初始化值为1
                incPosNum[i] = 1;
                descPosNum[i] = 1;
            }

            countIncPosition(inputNum, incPosNum);//递增数据
            //翻转输入数据
            List<Integer> inputNumList = Arrays.asList(inputNum);//使用工具类转换为集合后不能在集合中增加或删除元素,但是可以修改
            Collections.reverse(inputNumList);
            countIncPosition(inputNum, descPosNum);//递减数据
            //翻转计算出来的递减数据的位置数据descPosNum,保证递增递减出来的数据与源输入数据对应
            List<Integer> descPosNumList = Arrays.asList(descPosNum);
            Collections.reverse(descPosNumList);
            //此时incPosNum,descPosNum保存的数据即是源数据在递增和递减的时候对应的最大子串中的位置

            Integer[] maxPosition = new Integer[inputNum.length];
            for(int i=0;i<maxPosition.length;i++){
                //递增和递减中都计算了自己,所以重复了一次
                maxPosition[i] = incPosNum[i]+descPosNum[i]-1;
            }
            //排序该数所在队列人数,最后一个数即为此输入数据的最大队列人数
           Arrays.sort(maxPosition);
            System.out.println(nums-maxPosition[maxPosition.length-1]);
        }
    }

    private static void countIncPosition(Integer[] inputNum, Integer[] outputNum) {
        //计算当前i所在的数在最大递增子串中的位置
        for(int i=1;i<inputNum.length;i++){//从第二个数开始,第一个数在最大递增子串中的位置即为1
            for(int j=i-1;j>=0;j--){
                //inputNum[i]>inputNum[j],当前的数是否比它之前的数要大;
                // incPosNum[i]<incPosNum[j]+1,i前面的数已经记录了最大递增子串的位置,第一个数默认为1,故只需要比较本数据保存的递增位置是否小于前面之前保存的递增位置
                //本次处理数据inputNum[i]是这次处理的最大数据,所以他所在的位置一定比满足条件的前面的递增子串的位置要大
                if(inputNum[i]>inputNum[j] && outputNum[i]< outputNum[j]+1){
                    outputNum[i] = outputNum[j]+1;
                }
            }
        }
    }
}

题解分析:
首先计算每个数在最大递增子串中的位置
186 186 150 200 160 130 197 200 quene
1 1 1 2 2 1 3 4 递增计数

然后计算每个数在反向最大递减子串中的位置--->计算反向后每个数在最大递增子串中的位置
200 197 130 160 200 150 186 186 反向quene
1 1 1 2 3 2 3 3 递减计数

然后将每个数的递增计数和递减计数相加
186 186 150 200 160 130 197 200 quene
1 1 1 2 2 1 3 4 递增计数
3 3 2 3 2 1 1 1 递减计数
4 4 3 5 4 2 4 5 每个数在所在队列的人数+1(自己在递增和递减中被重复计算)
如160这个数
在递增队列中有2个人数
150 160
在递减队列中有2个人数
160 130
那么160所在队列中就有3个人
150 160 130
每个数的所在队列人数表达就是这个意思
总人数 - 该数所在队列人数 = 需要出队的人数

**动态规划:**
https://www.cnblogs.com/raichen/p/5772056.html

原文地址:https://www.cnblogs.com/InternetJava/p/12552444.html