题目描述
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<n
3 计算
对一个序列[0,k]来说,第i个元素的最长递增序列A(i)=maxLength{ [序列i,shouldAdd(A(j)),其中i<j<k]},当序列i<序列j,shouldAdd(A(j))=A(j),否则shouldAdd(A(j))=null
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)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。