【动态规划】数塔问题

动态规划-数塔问题

❓问题描述

给定一个数塔,从数塔的顶层出发,在每个结点可以选择向左或者向右走,一直走到最底层,要求找到一条路径,使得该路径上的数值和最大。以下图数塔为例。

【动态规划】数塔问题_第1张图片

如何求解

这是一道动态规划的简单例子,一般来说,求解动态规划问题的步骤分为以下三个阶段:

  1. 划分子问题,原问题可以划分成为多个子问题,每个子问题对应一个决策阶段,将问题的解决放在求解子问题上面。
  2. 确定动态规划函数,根据子问题之间的关系找到子问题满足的递推关系式(连接大问题和小问题的桥梁)。
  3. 填写表格,设计表格(相当于数组),根据题目选择用自底向上或者自顶向下的方式求解子问题的解并填表,实现动态规划过程

根据上面的例题实际分析一下

  1. 划分子问题

求从上到下的路径和最大,也就是从第1层到第5层的路径和最大,而求解这个问题的前提是第二层到第五层路径和最大,而求解第二层到第五层路径和问题由取决于求第三层到第五层的路径和最大,以此类推,将问题划分开来,这样我们就可以从最小的问题入手,也就是从第五层入手(第五层和也就是自身),最后求出第五层到第一层的最大路径和。

【动态规划】数塔问题_第2张图片

  1. 确定动态规划函数 手动模拟运算过程

第五层最大路径和就是自身,第四到第一层就通过动态规划方程来推导,以第四层的2为例,2和下一层的19和为21,2和下一层的7和为9,按照数值最大的要求,当然是要2和19,所以第四层第一个元素值就变成21,通过模拟运算,发现是那当前元素和下一层的左右两个元素分别求和,要的是最大的那个。通过分析可以写出第四层到第一层的动态规划方程。
d p [ i ] [ j ] = d p [ i ] [ j ] . . . . . . . . . . . 当 i = n , 0 < = i < n dp[i][j] = dp[i][j]...........当i=n,0<=idp[i][j]=dp[i][j]...........i=n,0<=i<n

d p [ i ] [ j ] = d p [ i ] [ j ] + M a x ( d p [ i + 1 ] [ j ] , d p [ i + 1 ] [ j + 1 ] ) . . . . . . . . . . . 0 < = i < n − 1 , 0 < = j < i dp[i][j]=dp[i][j]+Max(dp[i+1][j],dp[i+1][j+1])...........0<=idp[i][j]=dp[i][j]+Max(dp[i+1][j],dp[i+1][j+1])...........0<=i<n1,0<=j<i

由于用的是数组,所以第一层标记为0,i代表当前层,j代表该层第j个元素

  1. 填写表格

填写表格的过程已经在动态规划方程中实现了,直接是在原数组上进行覆盖,这样最后的第一层第一个元素就是结果。

⭐️记录路径

求出最大路径和的方法已经描述过了,至于求最小路径和的方法,只是在动态规划函数寻找最大改成寻找最小的即可,那么如何找出最大和的路径呢?

如果不记录路径,用一个dp数组就可以完成求解,但是要是记录路径,那么就需要多个数组来进行求解,dp数组还是从来存放最终的结果,arr数组是输入的值(用来最后输出路径用),path数组用来记录最大路径和的路径。

如何记录路径

  1. 二维数组记录

path数组定义为二维数组,path[i] [j],其值表示i层第j个元素的i+1层走向,也就是说path数组记录下了所有可能的路径,到时候根据顶层的path记录的值向下递推就可以求出路径(顶层的值是代表下一层的走向)。

  1. 一维数组记录

根据利用数组索引和值的配合,也可以通过一维数组实现记录,索引代表当前的层,值代表当前层的第几个元素每层决策完后的最大值就是确定的路径,把它记录下来即可。

代码实现

分别给出一维坐标和二维坐标记录求最大路径和的代码,求最小路径和在于动态规划方程和记录的方式不同。

input:
5
8
12 15
3 9 6
8 10 5 12
16 4 18 10 9
output:
最大路径和:600:81:152:93:104:18
一维坐标记录
package com.dp;
import java.util.Scanner;
/**
 * @Author Lunau
 * @Create 2022-03-29 10:46
 * @Description
 * @Result 60
 */
public class DataTower02 {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int len = sc.nextInt();
	    int[][] dp = new int[len][len];
	    int[][] arr = new int[len][len];
	    int[] path = new int[len];
		//自顶向下输入
        for(int i=0;i<len;i++) {
            for(int j=0;j<=i;j++) {
                arr[i][j] = sc.nextInt();
            }
       }
		//dp数组赋值
        for(int i=0;i<len;i++) {
        	dp[len-1][i] = arr[len-1][i];
        }
		//求最大路径和and最大路径
        for(int i=len-2;i>=0;i--) {
        	for(int j=0;j<=i;j++) {
				dp[i][j] = arr[i][j]+Math.max(dp[i+1][j],dp[i+1][j+1]);
        	}
			//求当前层的路径
			int max = 0;
			for(int j=0;j<=i+1;j++) {	//0到len-1层 所以是i+1
				if(dp[i+1][j]>max) {	//当前层的最大值就是需要走的路径
					max=dp[i+1][j];
					path[i+1]=j;	//记录路径坐标
				}
			}
        }
        System.out.println("最大路径和:"+dp[0][0]);
        /*for(int i=0;i
		//输出路径上的值
		for(int i=0;i<len;i++) {
			System.out.println("第"+i+"层:"+arr[i][path[i]]);
		}
	}
}
二维坐标记录
package com.dp;
import java.util.*;
public class DataTower02 {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int len = sc.nextInt();
	    int[][] dp = new int[len][len];
	    int[][] arr = new int[len][len];
	    int[][] path = new int[len][len];
		//自顶向下输入
        for(int i=0;i<len;i++) {
            for(int j=0;j<=i;j++) {
                arr[i][j] = sc.nextInt();
            }
       }
		//dp数组赋值
        for(int i=0;i<len;i++) {
        	dp[len-1][i] = arr[len-1][i];
        }
		//求最大路径和and最大路径
        for(int i=len-2;i>=0;i--) {
        	for(int j=0;j<=i;j++) {
				//当前层元素的下一层左右更大的赋值给当前层元素
				if(dp[i+1][j]>dp[i+1][j+1]) {
					dp[i][j] = dp[i+1][j] + arr[i][j];
					path[i][j]=j;	//本次决策选择下标为j的元素
				} else {
					dp[i][j] = dp[i+1][j+1] + arr[i][j];
					path[i][j]=j+1;	//本次决策选择下标为j+1的元素
				}
        	}
        }
        System.out.println("最大路径和:"+dp[0][0]);
		//输出路径上的值
		int tp = path[0][0];	//顶层记录的是下一层的走向
		System.out.println("第0层:"+arr[0][0]);
		for(int i=1;i<len;i++) {
			System.out.println("第"+i+"层:"+arr[i][tp]);
			tp=path[i][tp];
		}
	}
}

你可能感兴趣的:(算法,Java,java,动态规划,算法)