题目:数字三角形
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
或
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
上图给出了一个数字三角形。从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,你的任务就是找到最大的和。
路径上的每一步只能从一个数走到下一层和它最近的左边的那个数或者右边的那个数。此外,向左下走的次数与向右下走的次数相差不能超过 1。
思路
-
从 7 开始,往左下和右下有两种选择。如果选择左下,则把以 3 为顶的数字三角形看成一个新的问题,依次递归下去。递归需要能够处理所有子问题,那么如何标识每次问题中的数字三角形呢?用
(i, j)
。 -
此外,题中还有一个附加条件:“(最终)向左下走的次数与向右下走的次数相差不能超过 1”。所以,我引入了
(l, r)
去标记当前向左/右走的总步数。 -
引入记忆数组
memo[i][j]
:以(i, j)
数字为首的数字三角形求出的最大和。
解答
import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main {
static int n;
static int[][] nums;
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
//在此输入您的代码...
n = scan.nextInt();
nums = new int[n][n];
int[][] memo = new int[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j <= i; j++) {
nums[i][j] = scan.nextInt();
}
}
int res = findMax(0, 0, 0, 0, memo);
System.out.println(res);
scan.close();
}
public static int findMax(int i, int j, int l, int r, int[][] memo) {
if (i == n - 1) {
if (Math.abs(l - r) <= 1) return nums[i][j];
else return -10000;
}
if (memo[i + 1][j] == 0) memo[i + 1][j] = findMax(i + 1, j, l + 1, r, memo);
if (memo[i + 1][j + 1] == 0) memo[i + 1][j + 1] = findMax(i + 1, j + 1, l, r + 1, memo);
int leftRes = nums[i][j] + memo[i + 1][j];
int rightRes = nums[i][j] + memo[i + 1][j + 1];
return Math.max(leftRes, rightRes);
}
}