数字三角形求最大路径和的多种解法

问题描述:
  在数字三角形中寻找一条从顶部到底边的路径,使得路径上所经过的数字之和最大。路径上的每一步都只能往左下或者右下走,只需要求出这个最大和即可,不需要给出具体路径。
例如:
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

最大和为:30



解法一:
  递归。每一个点都有两个方向选择,所以递归树是二叉树。

代码如下:


import java.util.Scanner;

public class 数字三角形 {
	public static void main(String[] args){
		Scanner scanner = new Scanner(System.in);
		int n = scanner.nextInt();//表示行数
		int[][] arr	= new int[n][n];
		for(int i=0; i<n; i++){
			for(int j=0; j<=i; j++){
				arr[i][j] = scanner.nextInt();
			}
		}
		System.out.println(dfs(arr, 0, 0));
	}

	/*
	 * 自顶向下
	 */
	private static int dfs(int[][] arr, int row, int col) {
		if(row == arr.length-1){
			return arr[row][col];
		}
		int v1 = arr[row][col] + dfs(arr, row+1, col);//往左下走的结果
		int v2 = arr[row][col] + dfs(arr, row+1, col+1);//往右下走的结果
		return Math.max(v1, v2);
	}
}



解法二:
  用记忆型递归,在递归的基础上进行改进,因为在递归的过程中出现重复子问题;导致多次求解同一个子问题,为了提高效率在递归的过程中进行子问题解的记录。

代码如下:


import java.util.Arrays;
import java.util.Scanner;

public class 数字三角形 {
	public static void main(String[] args){
		Scanner scanner = new Scanner(System.in);
		int n = scanner.nextInt();//表示行数
		int[][] arr	= new int[n][n];
		int[][] vs = new int[n][n];//记录表
		for(int i=0; i<n; i++){//初始化记录表
			Arrays.fill(vs[i], -1);
		}
		for(int i=0; i<n; i++){
			for(int j=0; j<=i; j++){
				arr[i][j] = scanner.nextInt();
			}
		}
		System.out.println(dfs(arr, vs, 0, 0));
	}

	/*
	 * 自顶向下
	 */
	private static int dfs(int[][] arr, int[][] vs, int row, int col) {
		if(row == arr.length-1){
			return arr[row][col];
		}
		if(vs[row][col]>=0){//检查是否有过记录
			return vs[row][col];
		}
		int v1 = arr[row][col] + dfs(arr, vs, row+1, col);//往左下走的结果
		int v2 = arr[row][col] + dfs(arr, vs, row+1, col+1);//往右下走的结果
		vs[row][col] = Math.max(v1, v2);//返回前进行记录
		return Math.max(v1, v2);
	}
}


解法三:
  使用动态规划,和上面的递归不同的是,动态规划dp表建立的过程是自底向上的,而递归是自顶向下的。我们可以发现当走到最后一行时(也就是到达叶子结点时),dp表的值就等于节点本身的值,所以我们可以从底部向上推导。

代码如下:


import java.util.Arrays;
import java.util.Scanner;

public class 数字三角形 {
	public static void main(String[] args){
		Scanner scanner = new Scanner(System.in);
		int n = scanner.nextInt();//表示行数
		int[][] arr	= new int[n][n];
		int[][] dp = new int[n][n];//dp表
		for(int i=0; i<n; i++){
			for(int j=0; j<=i; j++){
				arr[i][j] = scanner.nextInt();
			}
		}
		System.out.println(dongGui(arr, dp));
	}

	/*
	 * 动态规划,自底向上
	 */
	private static int dongGui(int[][] arr, int[][] vs) {
		int n = arr.length;
		for(int j=0; j<n; j++){//初始化最后一行
			vs[n-1][j] = arr[n-1][j];
		}
		
		for(int i=n-2; i>=0; i--){//从倒数第二行开始
			for(int j=0; j<=i; j++){
				int v1 = arr[i][j] + vs[i+1][j];//左下方向的和
				int v2 = arr[i][j] + vs[i+1][j+1];//右下方向的和
				vs[i][j] = Math.max(v1, v2);
			}
		}
		return vs[0][0];
	}
}



总结:
  这题的递归和动态规划和01背包问题以及钢条分割问题很类似;只是这题在递归和建表的过程中是位置在变,前面两题是参数在变。不过算法思想都一样,这题如果想节省空间还可以使用滚动数组。因为dp表每求出一行,它的下面一行就没有用了。在建立dp表时只需要根据上一行而不是一整个表;所以可以用一个一维数组来表示dp表。

猜你喜欢

转载自blog.csdn.net/HC199854/article/details/104876813
今日推荐