问题描述:
在上面的数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大。
路径上的每一步都只能往左下或 右下走。只需要求出这个最大和即可,不必给出具体路径。 三角形的行数大于1小于等于100,数字为 0 - 99
输入:
三角形序列:[[7],[3,8],[8,1,0],[2,7,4,4],[4,5,2,6,5]]
输出:
最大和,走过的数字序列
1 思路
把三角形看做一个二叉树,根节点为第一层,往第i+1层走时,是在第i层的基础上增加i+1个可选项,用到达第i层的路径和加上可选项,将有最大和的可选项纳入路径
2 拆分子问题
第i层的每个节点都有其最大路径和,往第i+1层走时,每个节点都有两个选项,因此可以计算得到第i+1层每个节点的最大路径和
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}
4 代码
bottom-up DP
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 DP
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)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。