【中等】算法nodeJs:合唱队

206 阅读1分钟

描述

音乐课上,老师将 n 位同学排成一排。老师希望在不改变同学相对位置的前提下,从队伍中选出最少数量的同学,使得剩下的同学排成合唱队形。
记合唱队形中一共有 k 位同学,记编号为 1,2,…,k ,第 i 个人的身高为 hi​ 。要求:存在一位同学编号为 i(1<i<k) ,使得 h1​,h2​,…,hi−1​ 严格递增,且 hi+1​,hi+2​,…,hk​ 严格递减;更具体地,合唱队形呈 h1​<h2​<⋯<hi−1​<hi​ 、hi​>hi+1​>⋯>hk​ 。
你能帮助老师计算,最少需要出列多少位同学,才能使得剩下的同学排成合唱队形?

输入描述:

第一行输入一个整数 n(1≦n≦3000) 代表同学数量。
第二行输入 n 个整数 h1​,h2​,…,hn​(0≦hi​≦105) 代表每一位同学的身高。

输出描述:

输出一个整数,代表最少需要出列的同学数量。

image.png

讲解视频

const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;

void (async function () {
    // Write your code here
    const arr = [];
    while ((line = await readline())) {
        let tokens = line.split(" ");
        arr.push(tokens);
    }
    let lineArr = arr[1].map(Number); //学生身高
    const dpFn = (s) => { //构建一个动态规划一维数组函数
        let dp = Array(s.length).fill(1); //将每一项作为最高点,找存在最多子序列
        for (let i = 0; i < s.length; i++) {
            for (let j = 0; j < i; j++) {
                if (s[j] < s[i]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
        }
        return dp;
    };
    const leftArr = dpFn(lineArr); //从小到大
    const rightArr = dpFn([...lineArr].reverse()).reverse(); //从大到小
    const finArr = []; //组合成一个由低到高再到低
    const n = Number(arr[0][0]); //学生数量
    for (let i = 0; i < n; i++) {
        finArr[i] = leftArr[i] + rightArr[i] - 1;
    }
    console.log(n - Math.max(...finArr));
})();