数字三角形问题
描述:给定一个由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)); } }