動態規劃練習題彙總segmentfault
問題描述:this
在上面的數字三角形中尋找一條從頂部到底邊的路徑,使得路徑上所通過的數字之和最大。
路徑上的每一步都只能往左下或 右下走。只須要求出這個最大和便可,沒必要給出具體路徑。 三角形的行數大於1小於等於100,數字爲 0 - 99spa
輸入:
三角形序列:[[7],[3,8],[8,1,0],[2,7,4,4],[4,5,2,6,5]]code
輸出:
最大和,走過的數字序列blog
1 思路
把三角形看作一個二叉樹,根節點爲第一層,往第i+1層走時,是在第i層的基礎上增長i+1個可選項,用到達第i層的路徑和加上可選項,將有最大和的可選項歸入路徑ip
2 拆分子問題
第i層的每一個節點都有其最大路徑和,往第i+1層走時,每一個節點都有兩個選項,所以能夠計算獲得第i+1層每一個節點的最大路徑和get
3 計算
以w(i)(k)表示第i層的第k個節點的值,以S(i+1)做爲到達第i+1層的最大路徑和,以Sk(i+1)做爲第i+1層第k個節點的最大路徑和,其中1<=k<=i+1,S(i+1)=max{ Sk(i) + max{w(i+1)(k),w(i+1)(k+1)}, 其中1<=k<=i}it
4 代碼
bottom-up DPconsole
const numbers = [[7], [3, 8], [8, 1, 0], [2, 7, 4, 4], [4, 5, 2, 6, 5]]; class CalTree { constructor(numbers) { this.numbers = numbers; this.sums = numbers.map((item,rowIndex) => { return item.map((item, index) => { return rowIndex === 0 && index === 0 ? item : 0; }); }); this.path = numbers.map(item => { return item.map(item => item); }); } getSum() { for (let i = 0;i<this.numbers.length-1; i++){ let nextSums = this.sums[i+1]; let curSums = this.sums[i]; let nextItems = this.numbers[i + 1]; for (let index = 0; index < curSums.length; index++){ const item = curSums[index]; const temp1 = item + nextItems[index]; if (temp1 > nextSums[index]) { nextSums[index] = temp1; this.path[i+1][index] = [].concat(this.path[i][index], nextItems[index]); } const temp2 = item + nextItems[index + 1]; if (temp2 > nextSums[index + 1]) { nextSums[index + 1] = temp2; this.path[i+1][index + 1] = [].concat(this.path[i][index], nextItems[index + 1]); } } } const lastSumArr = this.sums[this.sums.length - 1]; const lastPathArr = this.path[this.sums.length - 1]; let maxSum = 0, maxSumPath = []; lastSumArr.forEach((item,index) => { if (item > maxSum) { maxSum = item; maxSumPath = lastPathArr[index]; } }); console.log("最大和是:", maxSum, ", 路徑爲:", maxSumPath.join(",")); } } new CalTree(numbers).getSum();
recurssive DPast
const numbers = [[7], [3, 8], [8, 1, 0], [2, 7, 4, 4], [4, 5, 2, 6, 5]]; class CalTree { constructor(numbers) { this.numbers = numbers; this.sums = numbers.map((item,rowIndex) => { return item.map((item, index) => { return rowIndex === 0 && index === 0 ? item : 0; }); }); this.path = [].concat(numbers); } getSumRecursive() { let lastArr = this.sums[this.sums.length - 1]; for (let i = 0; i < lastArr.length; i++){ this.cal(this.sums.length - 1, i); } const lastSumArr = this.sums[this.sums.length - 1]; const lastPathArr = this.path[this.sums.length - 1]; let maxSum = 0, maxSumPath = []; lastSumArr.forEach((item, index) => { if (item > maxSum) { maxSum = item; maxSumPath = lastPathArr[index]; } }); console.log("最大和是:", maxSum, ", 路徑爲:", maxSumPath.reverse().join(",")); } cal(depth, index) { if (this.sums[depth][index]) { return this.sums[depth][index]; } const curItem = this.numbers[depth][index]; const upSums = this.sums[depth - 1]; const temp = [].concat(this.path[depth][index]); if (index > 0) { const temp2 = this.cal(depth - 1, index - 1) + curItem; if (temp2 > this.sums[depth][index]) { this.sums[depth][index] = temp2; this.path[depth][index] = [].concat(temp, this.numbers[depth - 1][index - 1]); } } if (index < upSums.length) { const temp1 = this.cal(depth - 1, index) + curItem; if (temp1 > this.sums[depth][index]) { this.sums[depth][index] = temp1; this.path[depth][index] = [].concat(temp, this.numbers[depth - 1][index]); } } return this.sums[depth][index]; } } new CalTree(numbers).getSumRecursive();
5 時間複雜度令三角形的數字個數爲n,能夠發現,除了最底行,其餘行的每一個數字僅參與兩次計算,故時間複雜度爲O(n)