动态规划练习题汇总segmentfault
题目描述
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]
输出
就是最少须要几位同窗出列this
1 思路
寻找一个同窗,其左边同窗的身高递增序列+其右边同窗的身高递减序列是最长的,该问题难点在于如何计算一个序列的最长递增/递减序列code
2 拆分子问题
须要找到以序列中每一个元素开始到序列尾的序列的最长递增/递减序列,这些序列中最长的即为整个序列的最长递增/递减序列,子问题就是序列[i:n-1]的最长递增/递减序列,其中 0<=i<nget
3 计算
对一个序列[0,k]来讲,第i个元素的最长递增序列A(i)=maxLength{ [序列i,shouldAdd(A(j)),其中i<j<k]},当序列i<序列j,shouldAdd(A(j))=A(j),不然shouldAdd(A(j))=nullit
4 代码
bottom-up DPio
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 DPconsole
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)class