動態規劃練習題彙總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