赞
踩
【题目】:如下图所示的数字三角形,从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,和最大的路径称为最佳路径。编写一个程序求出最佳路径上的数字之和。 【使用备忘录法实现】
7
3 8
8 1 2
2 7 4 4
4 5 2 6 5
放在开头:本md会借助“数字三角形”(hnucm:1436: 数字三角形之备忘录法)浅谈DP,dfs,及递推。
极力推荐这位知乎网友写的文章,深入浅出,广受引用。
https://www.zhihu.com/question/23995189/answer/35429905
以下为关键句引用:
动态规划的本质不在于是递推或是递归,也不需要纠结是不是内存换时间。
理解动态规划并不需要数学公式介入,只是完全解释清楚需要点篇幅…首先需要明白哪些问题不是动态规划可以解决的,才能明白为神马需要动态规划。不过好处时顺便也就搞明白了递推贪心搜索和动规之间有什么关系,以及帮助那些总是把动规当成搜索解的同学建立动规的思路。
动态规划是对于 某一类问题 的解决方法!!重点在于如何鉴定“某一类问题”是动态规划可解的而不是纠结解决方法是递归还是递推!当你企图使用计算机解决一个问题是,其实就是在思考如何将这个问题表达成状态(用哪些变量存储哪些数据)以及如何在状态中转移(怎样根据一些变量计算出另一些变量)。所以所谓的空间复杂度就是为了支持你的计算所必需存储的状态最多有多少,所谓时间复杂度就是从初始状态到达最终状态中间需要多少步!
关于新词【状态】【状态转移】。//中午讲。
【后效性】
一个问题是该用递推、贪心、搜索还是动态规划,完全是由这个问题本身阶段间状态的转移方式决定的!
每个阶段只有一个状态->递推;
每个阶段的最优状态都是由上一个阶段的最优状态得到的->贪心;
每个阶段的最优状态是由之前所有阶段的状态的组合得到的->搜索;
每个阶段的最优状态可以从之前某个阶段的某个或某些状态直接得到而不管之前这个状态是如何得到的->动态规划。
每个阶段的最优状态可以从之前某个阶段的某个或某些状态直接得到
这个性质叫做最优子结构;
而不管之前这个状态是如何得到的
这个性质叫做无后效性。
import java.util.Scanner; //递推方法 /* * 思路整理: * 首先找出递推公式: * arr[i][j] = arr[i][j] + max(arr[i+1][j],arr[i+1][j+1]) * * 递推从下往上,一直计算到最上面的时候刚好是最大值 * * 数字三角形。如下图所示的数字三角形,从三角形的顶部到底部有很多条不同的路径。 对于每条路径,把路径上面的数加起来可以得到一个和,和最大的路径称为最佳路径。 编写一个程序求出最佳路径上的数字之和。 【注意:路径上的每一步只能从一个数走到下一层上和它最近的左边的数或者右边的数。】 7 3 8 8 1 2 2 7 4 4 4 5 2 6 5 * */ public class Triangle_recursion { public static void main(String[] args) { Scanner sn = new Scanner(System.in); int n = sn.nextInt(); int[][] arr = new int[n][n]; for(int i = 0;i < n;i++) { for(int j = 0;j <=i;j++) { arr[i][j] = sn.nextInt(); //数组初始化 } } int k = n-1; //数组下标最大 n-1 while(k > 0) { //k=0时不用计算 for(int i = 0;i <= k-1;i++) { arr[k-1][i] += Math.max(arr[k][i],arr[k][i+1]); } k--; } System.out.println(arr[0][0]); sn.close(); } }
import static java.lang.Math.max; import java.util.Scanner; //记忆化搜索 /* * 思路整理: * * v1 = 7+3+dfs(2,0) * v1 = 7 + dfs(1,0) * v2 = 7+3+dfs(2,1) * * * v1 = 7+8+dfs(2,1) * v2 = 7 + dfs(1,1) v2 = 7+8+dfs(2,2) 且最终取得最大最小值,所以最终得到的就是整个和的最大值,dfs既然是递归思想,就一定会遍历到最后 */ public class Triangle { static int n; //输入的常数n static int[][] triangle = new int[101][101]; //构造三角形 static int[][] rec = new int[101][101]; public static void main (String[] args) { Scanner sc = new Scanner(System.in); n = sc.nextInt(); for(int i = 0; i < n; i++){ for(int j = 0; j <= i; j++){ triangle[i][j] = sc.nextInt(); rec[i][j] = -1; //对所有位置做标记 } } int res = dfs(0, 0); //起点,从(0,0)开始 System.out.println(res); sc.close(); } public static int dfs(int row, int col){ //行 列 if(row == n - 1){ //最后一行 return triangle[row][col]; } if(rec[row][col] > 0){ //当前位置被计算过 return rec[row][col]; } int v1 = triangle[row][col] + dfs(row + 1, col); int v2 = triangle[row][col] + dfs(row + 1, col + 1); //进行当前节点调用完的值的记录,比如2调用完毕那么这个时候需要记录下2这个节点对应的记录数组的值 rec[row][col] = max(v1, v2); return rec[row][col]; } }
有后效性!覆盖,所以开两个数组。
import java.util.Scanner; /* 动态规划方法 核心思想:将问题拆分为若干个子问题 * * 思路整理: 找出重叠的子问题 * 首先写出状态转移方程: * arr[i][j] = arr[i][j] + max(arr[i+1][j],arr[i+1][j+1]) * * 必须由上往下计算,计算得最后一排的数值成为所有路径的最大值,最后遍历最后一行,找出最大值中的最大值,即得所解 * */ public class Triangle_DynamicProgramming { public static void main(String[] args) { Scanner sn = new Scanner(System.in); int n = sn.nextInt(); int[][] arr = new int[n][n]; for (int i = 0; i < n; i++) { for (int j = 0; j <= i; j++) { arr[i][j] = sn.nextInt(); // 数组初始化 } } // 利用动态规划特点:下一层的结果不影响上一层的结果,所以可以直接计算了将数组覆盖,不需要在new一个新的空间 // 数组第一层不变 for (int i = 1; i < n; i++) { for (int j = 0; j <= i; j++) { // 第二维 if (j == 0) // 第一个数 arr[i][j] += arr[i - 1][0]; else if (j == i) // 最后一个数 arr[i][j] += arr[i - 1][j - 1]; else // 中间的所有数 arr[i][j] += Math.max(arr[i - 1][j - 1], arr[i - 1][j]); } } int max = arr[n - 1][0]; for (int i = 0; i < n; i++) { if (max < arr[n - 1][i]) max = arr[n - 1][i]; } System.out.println(max); sn.close(); } }
在看完每种做法后建议回看知乎文章,仔细对照。
参照链接:
https://blog.csdn.net/LiLi_code/article/details/88555375:经典算法——数字三角形的三种解题方法:递推、记忆化搜索、动态规划;作者:LiLi_code
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。