数字三角形问题

描述:给定一个由n 行数字组成的数字三角形(例如下图)。输出从三角形的顶至底的一条路径,使该路径经过的数字总和最大。

(红色路径表示最优路径,结果为9+3+8+7+5 = 32)

算法基本思路:

显然用一个二维数组rec[0..n-1][0..n-1]可以保存该三角形作为输入(用二维数组对角线以下的位置保存三角形)。

对于任意rec[i][j],我们可以定义一个表m[i][j] 来表示以rec[i][j]作为当前路径的终点所得到的最优值。

对于m[i][j],要到达这个数字,必须经过前面的某个数字,可以通过计算前面的最优解来构造m[i][j]的最优解。

求解m[i][j]的最优解,可以通过子集的最优解构造,满足最优子结构,可以用动态规划。

1.求路径数字之和的最大值;

如前所述,这里的rec[i][j]有三种情况,

(1)rec[i][j]为行首即rec[i][0],那么要到达rec[i][0],只能经过rec[i-1][0],则m[i][0] = m[i-1][0] + rec[i][0];

(2)rec[i][j]为行尾即rec[i][i],那么要到达rec[i][i],只能经过rec[i-1][i-1],则m[i][i] = m[i-1][i-1] + rec[i][i];

(3)rec[i][j]为中部元素,此时到达rec[i][j]有两种方式,经过rec[i-1][j-1]或rec[i-1][j],则m[i][j] = max{m[i-1][j-1] + rec[i][j], m[i-1][j] + rec[i][j]}。

初始化m[0][0] = rec[0][0],自底向上构造表,可以得到以每个数字为终点的路径最优值。

此时,只需遍历以最后一行的数字为终点的各个最优值,取其最大值m[n-1][k],即是整个三角形的最优值。

2.获取整个构造路径。

在1.的基础上,添加表pre[i][j] 表示到达 rec[i][j] 的数字的上一个数字在定位,-1表示是rec[i-1][j-1],1表示是rec[i][j]。

然后以pre[n-1][k]为起点,逆向构造整条路径直至到达rec[0][0]即可。

public class RecMaxNum{

    public static int getRecMaxNum(int[][] rec){
        /*
        m[i][j]: the max value path which is ended of rec[i][j]
        pre[i][j]: the note of pre value index
        */
        int n = rec.length;
        int[][] m = new int[n][n];
        int[][] pre = new int[n][n];
        //init
        m[0][0] = rec[0][0];
        //dynamic programming
        for(int i = 1; i < n; i ++){
            m[i][0] = m[i-1][0] + rec[i][0];
            pre[i][0] = 1;
            for(int j = 1; j < i; j ++){
                if(m[i-1][j] + rec[i][j] < m[i-1][j-1] + rec[i][j]){
                    m[i][j] = m[i-1][j-1] + rec[i][j];
                    pre[i][j] = -1;
                }
                else{
                    m[i][j] = m[i-1][j] + rec[i][j];
                    pre[i][j] = 1;
                }
            }
            m[i][i] = m[i-1][i-1] + rec[i][i];
            pre[i][i] = -1;
        }
        int k = 0;
        for(int j = 1; j < n; j ++){
            if(m[n-1][k] < m[n-1][j]){
                k = j;
            }
        }
        //build the path
        StringBuffer s = new StringBuffer("");
        for(int i = n-1, j = k; i >= 0 && j >= 0;){
            s.insert(0, Integer.toString(rec[i][j]) + " ");
            if(pre[i][j] == -1){
                i --;
                j --;
            }
            else{
                i --;
            }
        }
        System.out.println(s);
        return m[n-1][k];
    }
    
    public static void main(String[] args) {
        int[][] rec = new int[5][5];
        for(int i = 0; i < 5; i ++){
            for(int j = 0; j <= i; j ++){
                rec[i][j] = (int)(Math.random() * 10);
                System.out.print(rec[i][j] + " ");
            }
            System.out.println();
        }
        System.out.println(getRecMaxNum(rec));
    }

}
Java
原文地址:https://www.cnblogs.com/7hat/p/3464100.html