合唱队形java_动态规划练习题-合唱队形

题目描述

N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学不交换位置就能排成合唱队形。

合唱队形定义:设K位同学从左到右依次编号为1, 2, …, K,他们的身高分别为T1, T2, …, TK,

则他们的身高满足T1 < T2 < … < Ti, Ti > Ti+1 > … > TK (1 <= i <= K)。

要求:已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

输入

同学的身高序列,序列长度为N,如[186,186,150,200,160,130,197,220]

输出

就是最少需要几位同学出列

1 思路

寻找一个同学,其左边同学的身高递增序列+其右边同学的身高递减序列是最长的,该问题难点在于如何计算一个序列的最长递增/递减序列

2 拆分子问题

需要找到以序列中每个元素开始到序列尾的序列的最长递增/递减序列,这些序列中最长的即为整个序列的最长递增/递减序列,子问题就是序列[i:n-1]的最长递增/递减序列,其中 0<=i

3 计算

对一个序列[0,k]来说,第i个元素的最长递增序列A(i)=maxLength{ [序列i,shouldAdd(A(j)),其中i

4 代码

bottom-up DP

const heightArray = [186,186,150,200,160,130,197,220];

class CalHeight {

constructor(options) {

this.heightArray = Array.isArray(options) ? options : [];

}

getScoreMemorize() {

let maxArr = [];

for (let i = 0; i < this.heightArray.length; i++) {

const left = this.getMaxAscend(this.heightArray.slice(0, i + 1));

const right = this.getMaxAscend(this.heightArray.slice(i, this.heightArray.length).reverse()).reverse();

const newArr = [...new Set(left.concat(right))];

if (newArr.length > maxArr.length) {

maxArr = newArr;

}

}

console.log(`最少需要 ${this.heightArray.length - maxArr.length} 位同学出列,留在队里的同学的身高是${maxArr.join()}`);

}

getMaxAscend(arr){

let ascendArr = [];

for (let i = arr.length - 1; i >= 0; i--){

let maxArr = [];

ascendArr[i] = {

id: i,

value: arr[i],

arr: [arr[i]]

};

for (let j = i + 1; j < arr.length; j++){

if (arr[i] < ascendArr[j].value) {

if (ascendArr[j].arr.length > maxArr.length) {

maxArr = ascendArr[j].arr;

}

}

}

ascendArr[i].arr = ascendArr[i].arr.concat(maxArr);

}

let result = [];

ascendArr.forEach(item => {

if (item.arr.length > result.length) {

result = item.arr;

}

});

return result;

}

}

new CalHeight(heightArray).getScoreMemorize();

recursive DP

const heightArray = [186,186,150,200,160,130,197,220];

class CalHeight {

constructor(options) {

this.heightArray = Array.isArray(options) ? options : [];

}

getScoreRecursive() {

let maxArr = [];

for (let i = 0; i < this.heightArray.length; i++) {

const left = this.getAscend(this.heightArray.slice(0, i + 1));

const right = this.getAscend(this.heightArray.slice(i, this.heightArray.length).reverse()).reverse();

const newArr = [...new Set(left.concat(right))];

if (newArr.length > maxArr.length) {

maxArr = newArr;

}

}

console.log(`最少需要 ${this.heightArray.length - maxArr.length} 位同学出列,留在队里的同学的身高是${maxArr.join()}`);

}

getAscend(arr) {

let max = [];

let memo = {};

this.getAscendRecursive(arr,0,memo);

Object.keys(memo).forEach(item => {

if (memo[item].arr.length > max.length) {

max = memo[item].arr;

}

});

return max;

}

getAscendRecursive(arr, n = 0, memo = {}) {

if (memo[n]) {

return memo[n];

}

if (n === arr.length - 1) {

memo[n] = { value: arr[n], arr: [arr[n]]};

return memo[n];

} else {

let max = [arr[n]];

for (let i = n + 1; i < arr.length; i++){

const next = this.getAscendRecursive(arr, i,memo);

if (arr[n] < next.value) {

if (next.arr.length >= max.length) {

max = [].concat(arr[n], next.arr);

}

}

}

memo[n] = { value: arr[n],arr: max };

return memo[n];

}

}

}

new CalHeight(heightArray).getScoreRecursive();

5 时间复杂度

对每个同学,都需要计算其左边同学的身高递增序列+其右边同学的身高递减序列;计算一个序列[0:k]的最长递增序列,需要计算序列中每个元素的最长递增序列,且计算第i个元素的最长递增序列时,需要进行k-i次比较,因此时间复杂度为O(n3)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值