网站开发 岗位职责,有效方法的小企业网站建设,中国建设监理网站,科技 杭州 网站建设class067 二维动态规划 code1 64. 最小路径和
// 最小路径和 // 给定一个包含非负整数的 m x n 网格 grid // 请找出一条从左上角到右下角的路径#xff0c;使得路径上的数字总和为最小。 // 说明#xff1a;每次只能向下或者向右移动一步。 // 测试链接 : https://leetcode…class067 二维动态规划 code1 64. 最小路径和
// 最小路径和 // 给定一个包含非负整数的 m x n 网格 grid // 请找出一条从左上角到右下角的路径使得路径上的数字总和为最小。 // 说明每次只能向下或者向右移动一步。 // 测试链接 : https://leetcode.cn/problems/minimum-path-sum/
dp[i][j]从(0,0)到(i,j)最小路径 和 dp[i][j]min(dp[i-1][j],dp[i][j-1])grid[i][j]
第0行dp[0][j-1]grid[0][j] 第0列dp[i-1][0]grid[i-1][0]
code1 暴力递归 code2 记忆化搜索 code3 动态规划 code4 空间压缩
package class067;// 最小路径和
// 给定一个包含非负整数的 m x n 网格 grid
// 请找出一条从左上角到右下角的路径使得路径上的数字总和为最小。
// 说明每次只能向下或者向右移动一步。
// 测试链接 : https://leetcode.cn/problems/minimum-path-sum/
public class Code01_MinimumPathSum {// 暴力递归public static int minPathSum1(int[][] grid) {return f1(grid, grid.length - 1, grid[0].length - 1);}// 从(0,0)到(i,j)最小路径和// 一定每次只能向右或者向下public static int f1(int[][] grid, int i, int j) {if (i 0 j 0) {return grid[0][0];}int up Integer.MAX_VALUE;int left Integer.MAX_VALUE;if (i - 1 0) {up f1(grid, i - 1, j);}if (j - 1 0) {left f1(grid, i, j - 1);}return grid[i][j] Math.min(up, left);}// 记忆化搜索public static int minPathSum2(int[][] grid) {int n grid.length;int m grid[0].length;int[][] dp new int[n][m];for (int i 0; i n; i) {for (int j 0; j m; j) {dp[i][j] -1;}}return f2(grid, grid.length - 1, grid[0].length - 1, dp);}public static int f2(int[][] grid, int i, int j, int[][] dp) {if (dp[i][j] ! -1) {return dp[i][j];}int ans;if (i 0 j 0) {ans grid[0][0];} else {int up Integer.MAX_VALUE;int left Integer.MAX_VALUE;if (i - 1 0) {up f2(grid, i - 1, j, dp);}if (j - 1 0) {left f2(grid, i, j - 1, dp);}ans grid[i][j] Math.min(up, left);}dp[i][j] ans;return ans;}// 严格位置依赖的动态规划public static int minPathSum3(int[][] grid) {int n grid.length;int m grid[0].length;int[][] dp new int[n][m];dp[0][0] grid[0][0];for (int i 1; i n; i) {dp[i][0] dp[i - 1][0] grid[i][0];}for (int j 1; j m; j) {dp[0][j] dp[0][j - 1] grid[0][j];}for (int i 1; i n; i) {for (int j 1; j m; j) {dp[i][j] Math.min(dp[i - 1][j], dp[i][j - 1]) grid[i][j];}}return dp[n - 1][m - 1];}// 严格位置依赖的动态规划 空间压缩技巧public static int minPathSum4(int[][] grid) {int n grid.length;int m grid[0].length;// 先让dp表变成想象中的表的第0行的数据int[] dp new int[m];dp[0] grid[0][0];for (int j 1; j m; j) {dp[j] dp[j - 1] grid[0][j];}for (int i 1; i n; i) {// i 1dp表变成想象中二维表的第1行的数据// i 2dp表变成想象中二维表的第2行的数据// i 3dp表变成想象中二维表的第3行的数据// ...// i n-1dp表变成想象中二维表的第n-1行的数据dp[0] grid[i][0];for (int j 1; j m; j) {dp[j] Math.min(dp[j - 1], dp[j]) grid[i][j];}}return dp[m - 1];}}
code2 79. 单词搜索
// 单词搜索无法改成动态规划 // 给定一个 m x n 二维字符网格 board 和一个字符串单词 word // 如果 word 存在于网格中返回 true 否则返回 false 。 // 单词必须按照字母顺序通过相邻的单元格内的字母构成 // 其中相邻单元格是那些水平相邻或垂直相邻的单元格 // 同一个单元格内的字母不允许被重复使用 // 测试链接 : https://leetcode.cn/problems/word-search/
code 递归
package class067;// 单词搜索无法改成动态规划
// 给定一个 m x n 二维字符网格 board 和一个字符串单词 word
// 如果 word 存在于网格中返回 true 否则返回 false 。
// 单词必须按照字母顺序通过相邻的单元格内的字母构成
// 其中相邻单元格是那些水平相邻或垂直相邻的单元格
// 同一个单元格内的字母不允许被重复使用
// 测试链接 : https://leetcode.cn/problems/word-search/
public class Code02_WordSearch {public static boolean exist(char[][] board, String word) {char[] w word.toCharArray();for (int i 0; i board.length; i) {for (int j 0; j board[0].length; j) {if (f(board, i, j, w, 0)) {return true;}}}return false;}// 因为board会改其中的字符// 用来标记哪些字符无法再用// 带路径的递归无法改成动态规划或者说没必要// 从(i,j)出发来到w[k]请问后续能不能把word走出来w[k...]public static boolean f(char[][] b, int i, int j, char[] w, int k) {if (k w.length) {return true;}if (i 0 || i b.length || j 0 || j b[0].length || b[i][j] ! w[k]) {return false;}// 不越界b[i][j] w[k]char tmp b[i][j];b[i][j] 0;boolean ans f(b, i - 1, j, w, k 1) || f(b, i 1, j, w, k 1) || f(b, i, j - 1, w, k 1)|| f(b, i, j 1, w, k 1);b[i][j] tmp;return ans;}}
code3 1143. 最长公共子序列
// 最长公共子序列 // 给定两个字符串text1和text2 // 返回这两个字符串的最长 公共子序列 的长度 // 如果不存在公共子序列返回0 // 两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列 // 测试链接 : https://leetcode.cn/problems/longest-common-subsequence/
dp[i][j]text1[前i个]和text2[前j个]最长公共子序列的长度 dp[i-1][j-1]1,text1[i-1]text2[j-1] max(dp[i-1][j],dp[i][j-1])
第0行 0 第0列 0
code1 递归 code2 递归 code3 记忆化搜索 code4 动态规划 code5 空间压缩
package class067;// 最长公共子序列
// 给定两个字符串text1和text2
// 返回这两个字符串的最长 公共子序列 的长度
// 如果不存在公共子序列返回0
// 两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列
// 测试链接 : https://leetcode.cn/problems/longest-common-subsequence/
public class Code03_LongestCommonSubsequence {public static int longestCommonSubsequence1(String str1, String str2) {char[] s1 str1.toCharArray();char[] s2 str2.toCharArray();int n s1.length;int m s2.length;return f1(s1, s2, n - 1, m - 1);}// s1[0....i1]与s2[0....i2]最长公共子序列长度public static int f1(char[] s1, char[] s2, int i1, int i2) {if (i1 0 || i2 0) {return 0;}int p1 f1(s1, s2, i1 - 1, i2 - 1);int p2 f1(s1, s2, i1 - 1, i2);int p3 f1(s1, s2, i1, i2 - 1);int p4 s1[i1] s2[i2] ? (p1 1) : 0;return Math.max(Math.max(p1, p2), Math.max(p3, p4));}// 为了避免很多边界讨论// 很多时候往往不用下标来定义尝试而是用长度来定义尝试// 因为长度最短是0而下标越界的话讨论起来就比较麻烦public static int longestCommonSubsequence2(String str1, String str2) {char[] s1 str1.toCharArray();char[] s2 str2.toCharArray();int n s1.length;int m s2.length;return f2(s1, s2, n, m);}// s1[前缀长度为len1]对应s2[前缀长度为len2]// 最长公共子序列长度public static int f2(char[] s1, char[] s2, int len1, int len2) {if (len1 0 || len2 0) {return 0;}int ans;if (s1[len1 - 1] s2[len2 - 1]) {ans f2(s1, s2, len1 - 1, len2 - 1) 1;} else {ans Math.max(f2(s1, s2, len1 - 1, len2), f2(s1, s2, len1, len2 - 1));}return ans;}// 记忆化搜索public static int longestCommonSubsequence3(String str1, String str2) {char[] s1 str1.toCharArray();char[] s2 str2.toCharArray();int n s1.length;int m s2.length;int[][] dp new int[n 1][m 1];for (int i 0; i n; i) {for (int j 0; j m; j) {dp[i][j] -1;}}return f3(s1, s2, n, m, dp);}public static int f3(char[] s1, char[] s2, int len1, int len2, int[][] dp) {if (len1 0 || len2 0) {return 0;}if (dp[len1][len2] ! -1) {return dp[len1][len2];}int ans;if (s1[len1 - 1] s2[len2 - 1]) {ans f3(s1, s2, len1 - 1, len2 - 1, dp) 1;} else {ans Math.max(f3(s1, s2, len1 - 1, len2, dp), f3(s1, s2, len1, len2 - 1, dp));}dp[len1][len2] ans;return ans;}// 严格位置依赖的动态规划public static int longestCommonSubsequence4(String str1, String str2) {char[] s1 str1.toCharArray();char[] s2 str2.toCharArray();int n s1.length;int m s2.length;int[][] dp new int[n 1][m 1];for (int len1 1; len1 n; len1) {for (int len2 1; len2 m; len2) {if (s1[len1 - 1] s2[len2 - 1]) {dp[len1][len2] 1 dp[len1 - 1][len2 - 1];} else {dp[len1][len2] Math.max(dp[len1 - 1][len2], dp[len1][len2 - 1]);}}}return dp[n][m];}// 严格位置依赖的动态规划 空间压缩public static int longestCommonSubsequence5(String str1, String str2) {char[] s1, s2;if (str1.length() str2.length()) {s1 str1.toCharArray();s2 str2.toCharArray();} else {s1 str2.toCharArray();s2 str1.toCharArray();}int n s1.length;int m s2.length;int[] dp new int[m 1];for (int len1 1; len1 n; len1) {int leftUp 0, backup;for (int len2 1; len2 m; len2) {backup dp[len2];if (s1[len1 - 1] s2[len2 - 1]) {dp[len2] 1 leftUp;} else {dp[len2] Math.max(dp[len2], dp[len2 - 1]);}leftUp backup;}}return dp[m];}}
code4 516. 最长回文子序列
// 最长回文子序列 // 给你一个字符串 s 找出其中最长的回文子序列并返回该序列的长度 // 测试链接 : https://leetcode.cn/problems/longest-palindromic-subsequence/
dp[i][j]从[i,j]字符中有最长回文子序列的长度 1,ij 1/2,s[i]s[j],i1j 2dp[i1][j-1],s[i]s[j] max(dp[i1][j],dp[i][j-1])
从左到右从下到上
code1 递归 code2 记忆化搜索 code3 动态规划 code4 空间压缩
package class067;// 最长回文子序列
// 给你一个字符串 s 找出其中最长的回文子序列并返回该序列的长度
// 测试链接 : https://leetcode.cn/problems/longest-palindromic-subsequence/
public class Code04_LongestPalindromicSubsequence {// 最长回文子序列问题可以转化成最长公共子序列问题// 不过这里讲述区间动态规划的思路// 区间dp还会有单独的视频做详细讲述public static int longestPalindromeSubseq1(String str) {char[] s str.toCharArray();int n s.length;return f1(s, 0, n - 1);}// s[l...r]最长回文子序列长度// l rpublic static int f1(char[] s, int l, int r) {if (l r) {return 1;}if (l 1 r) {return s[l] s[r] ? 2 : 1;}if (s[l] s[r]) {return 2 f1(s, l 1, r - 1);} else {return Math.max(f1(s, l 1, r), f1(s, l, r - 1));}}public static int longestPalindromeSubseq2(String str) {char[] s str.toCharArray();int n s.length;int[][] dp new int[n][n];return f2(s, 0, n - 1, dp);}public static int f2(char[] s, int l, int r, int[][] dp) {if (l r) {return 1;}if (l 1 r) {return s[l] s[r] ? 2 : 1;}if (dp[l][r] ! 0) {return dp[l][r];}int ans;if (s[l] s[r]) {ans 2 f2(s, l 1, r - 1, dp);} else {ans Math.max(f2(s, l 1, r, dp), f2(s, l, r - 1, dp));}dp[l][r] ans;return ans;}public static int longestPalindromeSubseq3(String str) {char[] s str.toCharArray();int n s.length;int[][] dp new int[n][n];for (int l n - 1; l 0; l--) {dp[l][l] 1;if (l 1 n) {dp[l][l 1] s[l] s[l 1] ? 2 : 1;}for (int r l 2; r n; r) {if (s[l] s[r]) {dp[l][r] 2 dp[l 1][r - 1];} else {dp[l][r] Math.max(dp[l 1][r], dp[l][r - 1]);}}}return dp[0][n - 1];}public static int longestPalindromeSubseq4(String str) {char[] s str.toCharArray();int n s.length;int[] dp new int[n];for (int l n - 1, leftDown 0, backup; l 0; l--) {// dp[l] : 想象中的dp[l][l]dp[l] 1;if (l 1 n) {leftDown dp[l 1];// dp[l1] : 想象中的dp[l][l1]dp[l 1] s[l] s[l 1] ? 2 : 1;}for (int r l 2; r n; r) {backup dp[r];if (s[l] s[r]) {dp[r] 2 leftDown;} else {dp[r] Math.max(dp[r], dp[r - 1]);}leftDown backup;}}return dp[n - 1];}}
code5 节点数为n高度不大于m的二叉树个数
// 节点数为n高度不大于m的二叉树个数 // 现在有n个节点计算出有多少个不同结构的二叉树 // 满足节点个数为n且树的高度不超过m的方案 // 因为答案很大所以答案需要模上1000000007后输出 // 测试链接 : https://www.nowcoder.com/practice/aaefe5896cce4204b276e213e725f3ea // 请同学们务必参考如下代码中关于输入、输出的处理 // 这是输入输出处理效率很高的写法 // 提交以下所有代码把主类名改成Main可以直接通过
思路就是头占1个左右占[0,n-1] dp[i][j]节点数为i高度不大于j的二叉树个数 ∑dp[k][j-1]*dp[i-k-1][j-1],(0ki)
package class067;// 节点数为n高度不大于m的二叉树个数
// 现在有n个节点计算出有多少个不同结构的二叉树
// 满足节点个数为n且树的高度不超过m的方案
// 因为答案很大所以答案需要模上1000000007后输出
// 测试链接 : https://www.nowcoder.com/practice/aaefe5896cce4204b276e213e725f3ea
// 请同学们务必参考如下代码中关于输入、输出的处理
// 这是输入输出处理效率很高的写法
// 提交以下所有代码把主类名改成Main可以直接通过import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;public class Code05_NodenHeightNotLargerThanm {public static void main(String[] args) throws IOException {BufferedReader br new BufferedReader(new InputStreamReader(System.in));StreamTokenizer in new StreamTokenizer(br);PrintWriter out new PrintWriter(new OutputStreamWriter(System.out));while (in.nextToken() ! StreamTokenizer.TT_EOF) {int n (int) in.nval;in.nextToken();int m (int) in.nval;out.println(compute3(n, m));}out.flush();out.close();br.close();}public static int MAXN 51;public static int MOD 1000000007;// 记忆化搜索public static long[][] dp1 new long[MAXN][MAXN];static {for (int i 0; i MAXN; i) {for (int j 0; j MAXN; j) {dp1[i][j] -1;}}}// 二叉树节点数为n// 高度不能超过m// 结构数返回// 记忆化搜索public static int compute1(int n, int m) {if (n 0) {return 1;}// n 0if (m 0) {return 0;}if (dp1[n][m] ! -1) {return (int) dp1[n][m];}long ans 0;// n个点头占掉1个for (int k 0; k n; k) {// 一共n个节点头节点已经占用了1个名额// 如果左树占用k个那么右树就占用i-k-1个ans (ans ((long) compute1(k, m - 1) * compute1(n - k - 1, m - 1)) % MOD) % MOD;}dp1[n][m] ans;return (int) ans;}// 严格位置依赖的动态规划public static long[][] dp2 new long[MAXN][MAXN];public static int compute2(int n, int m) {for (int j 0; j m; j) {dp2[0][j] 1;}for (int i 1; i n; i) {for (int j 1; j m; j) {dp2[i][j] 0;for (int k 0; k i; k) {// 一共i个节点头节点已经占用了1个名额// 如果左树占用k个那么右树就占用i-k-1个dp2[i][j] (dp2[i][j] dp2[k][j - 1] * dp2[i - k - 1][j - 1] % MOD) % MOD;}}}return (int) dp2[n][m];}// 空间压缩public static long[] dp3 new long[MAXN];public static int compute3(int n, int m) {dp3[0] 1;for (int i 1; i n; i) {dp3[i] 0;}for (int j 1; j m; j) {// 根据依赖一定要先枚举列for (int i n; i 1; i--) {// 再枚举行而且i不需要到达0i1即可dp3[i] 0;for (int k 0; k i; k) {// 枚举dp3[i] (dp3[i] dp3[k] * dp3[i - k - 1] % MOD) % MOD;}}}return (int) dp3[n];}}
code6 329. 矩阵中的最长递增路径
// 矩阵中的最长递增路径 // 给定一个 m x n 整数矩阵 matrix 找出其中 最长递增路径 的长度 // 对于每个单元格你可以往上下左右四个方向移动 // 你 不能 在 对角线 方向上移动或移动到 边界外即不允许环绕 // 测试链接 : https://leetcode.cn/problems/longest-increasing-path-in-a-matrix/
dp[i][j]从(i,j)出发到达的最长递增路径 max(dp[i-1][j],dp[i1][j],dp[i][j-1],dp[i][j1])1,只有对应大才能走
code1 递归 code2 记忆化搜索
package class067;// 矩阵中的最长递增路径
// 给定一个 m x n 整数矩阵 matrix 找出其中 最长递增路径 的长度
// 对于每个单元格你可以往上下左右四个方向移动
// 你 不能 在 对角线 方向上移动或移动到 边界外即不允许环绕
// 测试链接 : https://leetcode.cn/problems/longest-increasing-path-in-a-matrix/
public class Code06_LongestIncreasingPath {public static int longestIncreasingPath1(int[][] grid) {int ans 0;for (int i 0; i grid.length; i) {for (int j 0; j grid[0].length; j) {ans Math.max(ans, f1(grid, i, j));}}return ans;}// 从(i,j)出发能走出来多长的递增路径返回最长长度public static int f1(int[][] grid, int i, int j) {int next 0;if (i 0 grid[i][j] grid[i - 1][j]) {next Math.max(next, f1(grid, i - 1, j));}if (i 1 grid.length grid[i][j] grid[i 1][j]) {next Math.max(next, f1(grid, i 1, j));}if (j 0 grid[i][j] grid[i][j - 1]) {next Math.max(next, f1(grid, i, j - 1));}if (j 1 grid[0].length grid[i][j] grid[i][j 1]) {next Math.max(next, f1(grid, i, j 1));}return next 1;}public static int longestIncreasingPath2(int[][] grid) {int n grid.length;int m grid[0].length;int[][] dp new int[n][m];int ans 0;for (int i 0; i n; i) {for (int j 0; j m; j) {ans Math.max(ans, f2(grid, i, j, dp));}}return ans;}public static int f2(int[][] grid, int i, int j, int[][] dp) {if (dp[i][j] ! 0) {return dp[i][j];}int next 0;if (i 0 grid[i][j] grid[i - 1][j]) {next Math.max(next, f2(grid, i - 1, j, dp));}if (i 1 grid.length grid[i][j] grid[i 1][j]) {next Math.max(next, f2(grid, i 1, j, dp));}if (j 0 grid[i][j] grid[i][j - 1]) {next Math.max(next, f2(grid, i, j - 1, dp));}if (j 1 grid[0].length grid[i][j] grid[i][j 1]) {next Math.max(next, f2(grid, i, j 1, dp));}dp[i][j] next 1;return next 1;}}