题目输入:

 输入格式:

                  5      //表示三角形的行数    接下来输入三角形

                  7

                3   8

             8   1   0

           2   7   4   4

         4   5   2   6   5

在上面的数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大。路径上的每一步都只能往左下或 右下走。只需要求出这个最大和即可,不必给出具体路径。 三角形的行数大于1小于等于100,数字为 0 - 99。

输出结果:30

看到这个三角输入,首先需要用二维数组进行记录,记为D,那么可以看出可以使用递归的方法进行求解,matrix[line][row]表示当前的位置,其中line表示行,row表示列,那么最大值就应该是当前的值+下一行所对应的两个值中的最大的那个,也就是maxSum(matrix, line + 1, row, len)和maxSum(matrix, line + 1, row + 1, len),其中maxSum()表示对二维数组某个位置开始从上网下的求和,len表示的也是行数,是终止条件,也就是当line==len的时候,递归到最后一行的时候结束。核心方法如下:

public static int maxSum(int[][] matrix, int line, int row, int len) {
        if (line == len) {
            return matrix[line][row];//当递归到最后一行直接返回
        }
        int cur = matrix[line][row];//保存当前位置的值
        int x = maxSum(matrix, line + 1, row, len);//计算左儿子的值
        int y = maxSum(matrix, line + 1, row + 1, len);//计算右儿子的值
        return Math.max(x, y) + cur;//返回左右两个儿子中最大的值+当前值
    }

但是仔细看就可以看出来在从上往下进行递归的过程中计算存在大量重复,越往上计算的次数越多,所以我们就要换种思路,采用动态规划来求解。

动态规划:

                  7

                3   8

             8   1   0

           2   7   4   4

         4   5   2   6   5

粘贴过来方便观察,现在换种思路,从下往上推,初始化一个和最后一行一样大小的数组,也将值复制给它,也就是初始化为max=[4,5,2,6,5],首先我们找到max中相邻两个节点中最大的那个,接着加上相邻两个元素的父节点,例如最后一行4,5的父节点是2,那么第一次循环后就得到max=[7,5,2,6,5],接着是5,2和7,继续更新为max=[7,12,2,6,5]...以此类推。这样就可以不断的向上一层进行循环,一直到第一层,最终max[0]位置的元素便是最大值,核心方法:

public static int maxSum2(int[][] matrix) {
        int len = matrix.length - 1;
        int[] max = matrix[len];//最后一层的元素
        for (int i = len-1; i >= 0; i--) {//从倒数第二行开始循环,因为最后一行我们已经初始化过了
            for (int j = 0; j <= i; j++) {
                //动态数组的值就是当前行的元素相邻的最大值+上一层多对应的元素。
                max[j] = Math.max(max[j], max[j + 1]) + matrix[i][j];
            }
        }
        return max[0];
    }

命令行输入:

5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

完整代码:

package arithmetic;

import java.util.Scanner;

/**
 * Created by Hollake on 2019\7\19 0019 13:23.
 */
public class DpTwo {
    public static void main(String[] args) {
//        int[][] matrix = {{ 7}, { 3, 8}, { 8, 1, 0}, { 2, 7, 4, 4}, { 4, 5, 2, 6, 5}};
        Scanner sc = new Scanner(System.in);
        while ( sc.hasNext() ) {
            int line = sc.nextInt();
            int[][] matrix = new int[line][line];
            for (int i = 0; i < line; i++) {
                for (int j = 0; j < i + 1; j++) {
                    matrix[i][j] = sc.nextInt();
                }
            }
            System.out.println(maxSum(matrix, 0, 0, line-1));
            System.out.println(maxSum2(matrix));
        }
    }
//  递归
    public static int maxSum(int[][] matrix, int line, int row, int len) {
        if (line == len) {
            return matrix[line][row];//当递归到最后一行直接返回
        }
        int cur = matrix[line][row];//保存当前位置的值
        int x = maxSum(matrix, line + 1, row, len);//计算左儿子的值
        int y = maxSum(matrix, line + 1, row + 1, len);//计算右儿子的值
        return Math.max(x, y) + cur;//返回左右两个儿子中最大的值+当前值
    }
//  动态规划
    public static int maxSum2(int[][] matrix) {
        int len = matrix.length - 1;
        int[] max = matrix[len];//最后一层的元素
        for (int i = len-1; i >= 0; i--) {//从倒数第二行开始循环,因为最后一行我们已经初始化过了
            for (int j = 0; j <= i; j++) {
                //动态数组的值就是当前行的元素相邻的最大值+上一层多对应的元素。
                max[j] = Math.max(max[j], max[j + 1]) + matrix[i][j];
            }
        }
        return max[0];
    }
}