From ad0fd45cfb2c950f763a180a72452e6ce5a2f383 Mon Sep 17 00:00:00 2001 From: krahets Date: Tue, 11 Jul 2023 01:08:26 +0800 Subject: [PATCH] Add Java and C++ code for the chapter of DP. --- .../CMakeLists.txt | 4 +- .../chapter_dynamic_programming/knapsack.cpp | 109 +++++++++++++++ .../min_path_sum.cpp | 116 ++++++++++++++++ .../min_path_sum.cs | 13 +- .../chapter_dynamic_programming/knapsack.java | 116 ++++++++++++++++ .../min_path_sum.java | 125 ++++++++++++++++++ .../chapter_dynamic_programming/knapsack.py | 10 +- .../min_path_sum.py | 18 +-- .../dp_solution_pipeline.md | 2 +- 9 files changed, 494 insertions(+), 19 deletions(-) create mode 100644 codes/cpp/chapter_dynamic_programming/knapsack.cpp create mode 100644 codes/cpp/chapter_dynamic_programming/min_path_sum.cpp create mode 100644 codes/java/chapter_dynamic_programming/knapsack.java create mode 100644 codes/java/chapter_dynamic_programming/min_path_sum.java diff --git a/codes/cpp/chapter_dynamic_programming/CMakeLists.txt b/codes/cpp/chapter_dynamic_programming/CMakeLists.txt index ff0a7abdb..1524b2cf4 100644 --- a/codes/cpp/chapter_dynamic_programming/CMakeLists.txt +++ b/codes/cpp/chapter_dynamic_programming/CMakeLists.txt @@ -2,4 +2,6 @@ add_executable(climbing_stairs_backtrack climbing_stairs_backtrack.cpp) add_executable(climbing_stairs_dfs climbing_stairs_dfs.cpp) add_executable(climbing_stairs_dfs_mem climbing_stairs_dfs_mem.cpp) add_executable(climbing_stairs_dp climbing_stairs_dp.cpp) -add_executable(min_cost_climbing_stairs_dp min_cost_climbing_stairs_dp.cpp) \ No newline at end of file +add_executable(min_cost_climbing_stairs_dp min_cost_climbing_stairs_dp.cpp) +add_executable(min_path_sum min_path_sum.cpp) +add_executable(knapsack knapsack.cpp) \ No newline at end of file diff --git a/codes/cpp/chapter_dynamic_programming/knapsack.cpp b/codes/cpp/chapter_dynamic_programming/knapsack.cpp new file mode 100644 index 000000000..59e7cb6b8 --- /dev/null +++ b/codes/cpp/chapter_dynamic_programming/knapsack.cpp @@ -0,0 +1,109 @@ +#include +#include +#include + +using namespace std; + +/* 0-1 背包:暴力搜索 */ +int knapsackDFS(vector &wgt, vector &val, int i, int c) { + // 若已选完所有物品或背包无容量,则返回价值 0 + if (i == 0 || c == 0) { + return 0; + } + // 若超过背包容量,则只能不放入背包 + if (wgt[i - 1] > c) { + return knapsackDFS(wgt, val, i - 1, c); + } + // 计算不放入和放入物品 i 的最大价值 + int no = knapsackDFS(wgt, val, i - 1, c); + int yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; + // 返回两种方案中价值更大的那一个 + return max(no, yes); +} + +/* 0-1 背包:记忆化搜索 */ +int knapsackDFSMem(vector &wgt, vector &val, vector> &mem, int i, int c) { + // 若已选完所有物品或背包无容量,则返回价值 0 + if (i == 0 || c == 0) { + return 0; + } + // 若已有记录,则直接返回 + if (mem[i][c] != -1) { + return mem[i][c]; + } + // 若超过背包容量,则只能不放入背包 + if (wgt[i - 1] > c) { + return knapsackDFSMem(wgt, val, mem, i - 1, c); + } + // 计算不放入和放入物品 i 的最大价值 + int no = knapsackDFSMem(wgt, val, mem, i - 1, c); + int yes = knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; + // 记录并返回两种方案中价值更大的那一个 + mem[i][c] = max(no, yes); + return mem[i][c]; +} + +/* 0-1 背包:动态规划 */ +int knapsackDP(vector &wgt, vector &val, int cap) { + int n = wgt.size(); + // 初始化 dp 表 + vector> dp(n + 1, vector(cap + 1, 0)); + // 状态转移 + for (int i = 1; i <= n; i++) { + for (int c = 1; c <= cap; c++) { + if (wgt[i - 1] > c) { + // 若超过背包容量,则不选物品 i + dp[i][c] = dp[i - 1][c]; + } else { + // 不选和选物品 i 这两种方案的较大值 + dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[n][cap]; +} + +/* 0-1 背包:状态压缩后的动态规划 */ +int knapsackDPComp(vector &wgt, vector &val, int cap) { + int n = wgt.size(); + // 初始化 dp 表 + vector dp(cap + 1, 0); + // 状态转移 + for (int i = 1; i <= n; i++) { + // 倒序遍历 + for (int c = cap; c >= 1; c--) { + if (wgt[i - 1] <= c) { + // 不选和选物品 i 这两种方案的较大值 + dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[cap]; +} + +/* Driver Code */ +int main() { + vector wgt = {10, 20, 30, 40, 50}; + vector val = {50, 120, 150, 210, 240}; + int cap = 50; + int n = wgt.size(); + + // 暴力搜索 + int res = knapsackDFS(wgt, val, n, cap); + cout << "不超过背包容量的最大物品价值为 " << res << endl; + + // 记忆化搜索 + vector> mem(n + 1, vector(cap + 1, -1)); + res = knapsackDFSMem(wgt, val, mem, n, cap); + cout << "不超过背包容量的最大物品价值为 " << res << endl; + + // 动态规划 + res = knapsackDP(wgt, val, cap); + cout << "不超过背包容量的最大物品价值为 " << res << endl; + + // 状态压缩后的动态规划 + res = knapsackDPComp(wgt, val, cap); + cout << "不超过背包容量的最大物品价值为 " << res << endl; + + return 0; +} diff --git a/codes/cpp/chapter_dynamic_programming/min_path_sum.cpp b/codes/cpp/chapter_dynamic_programming/min_path_sum.cpp new file mode 100644 index 000000000..466adebfa --- /dev/null +++ b/codes/cpp/chapter_dynamic_programming/min_path_sum.cpp @@ -0,0 +1,116 @@ +/** + * File: min_path_sum.cpp + * Created Time: 2023-07-10 + * Author: Krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* 最小路径和:暴力搜索 */ +int minPathSumDFS(vector> &grid, int i, int j) { + // 若为左上角单元格,则终止搜索 + if (i == 0 && j == 0) { + return grid[0][0]; + } + // 若行列索引越界,则返回 +∞ 代价 + if (i < 0 || j < 0) { + return INT_MAX; + } + // 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价 + int left = minPathSumDFS(grid, i - 1, j); + int up = minPathSumDFS(grid, i, j - 1); + // 返回从左上角到 (i, j) 的最小路径代价 + return min(left, up) != INT_MAX ? min(left, up) + grid[i][j] : INT_MAX; +} + +/* 最小路径和:记忆化搜索 */ +int minPathSumDFSMem(vector> &grid, vector> &mem, int i, int j) { + // 若为左上角单元格,则终止搜索 + if (i == 0 && j == 0) { + return grid[0][0]; + } + // 若行列索引越界,则返回 +∞ 代价 + if (i < 0 || j < 0) { + return INT_MAX; + } + // 若已有记录,则直接返回 + if (mem[i][j] != -1) { + return mem[i][j]; + } + // 左边和上边单元格的最小路径代价 + int left = minPathSumDFSMem(grid, mem, i - 1, j); + int up = minPathSumDFSMem(grid, mem, i, j - 1); + // 记录并返回左上角到 (i, j) 的最小路径代价 + mem[i][j] = min(left, up) != INT_MAX ? min(left, up) + grid[i][j] : INT_MAX; + return mem[i][j]; +} + +/* 最小路径和:动态规划 */ +int minPathSumDP(vector> &grid) { + int n = grid.size(), m = grid[0].size(); + // 初始化 dp 表 + vector> dp(n, vector(m)); + dp[0][0] = grid[0][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++) { + dp[i][0] = dp[i - 1][0] + grid[i][0]; + } + // 状态转移:其余行列 + for (int i = 1; i < n; i++) { + for (int j = 1; j < m; j++) { + dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; + } + } + return dp[n - 1][m - 1]; +} + +/* 最小路径和:状态压缩后的动态规划 */ +int minPathSumDPComp(vector> &grid) { + int n = grid.size(), m = grid[0].size(); + // 初始化 dp 表 + vector dp(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++) { + // 状态转移:首列 + dp[0] = dp[0] + grid[i][0]; + // 状态转移:其余列 + for (int j = 1; j < m; j++) { + dp[j] = min(dp[j - 1], dp[j]) + grid[i][j]; + } + } + return dp[m - 1]; +} + +/* Driver Code */ +int main() { + vector> grid = {{1, 3, 1, 5}, {2, 2, 4, 2}, {5, 3, 2, 1}, {4, 3, 5, 2}}; + int n = grid.size(), m = grid[0].size(); + + // 暴力搜索 + int res = minPathSumDFS(grid, n - 1, m - 1); + cout << "从左上角到右下角的最小路径和为 " << res << endl; + + // 记忆化搜索 + vector> mem(n, vector(m, -1)); + res = minPathSumDFSMem(grid, mem, n - 1, m - 1); + cout << "从左上角到右下角的最小路径和为 " << res << endl; + + // 动态规划 + res = minPathSumDP(grid); + cout << "从左上角到右下角的最小路径和为 " << res << endl; + + // 状态压缩后的动态规划 + res = minPathSumDPComp(grid); + cout << "从左上角到右下角的最小路径和为 " << res << endl; + + return 0; +} diff --git a/codes/csharp/chapter_dynamic_programming/min_path_sum.cs b/codes/csharp/chapter_dynamic_programming/min_path_sum.cs index 183bac467..dee5b497f 100644 --- a/codes/csharp/chapter_dynamic_programming/min_path_sum.cs +++ b/codes/csharp/chapter_dynamic_programming/min_path_sum.cs @@ -104,7 +104,8 @@ public class min_path_sum { int n = grid.Length, m = grid[0].Length; // 暴力搜索 - Console.WriteLine(minPathSumDFS(grid, n - 1, m - 1)); + int res = minPathSumDFS(grid, n - 1, m - 1); + Console.WriteLine("从左上角到右下角的做小路径和为 " + res); // 记忆化搜索 int[][] mem = new int[n][]; @@ -112,13 +113,15 @@ public class min_path_sum { mem[i] = new int[m]; Array.Fill(mem[i], -1); } - - Console.WriteLine(minPathSumDFSMem(grid, mem, n - 1, m - 1)); + res = minPathSumDFSMem(grid, mem, n - 1, m - 1); + Console.WriteLine("从左上角到右下角的做小路径和为 " + res); // 动态规划 - Console.WriteLine(minPathSumDP(grid)); + res = minPathSumDP(grid); + Console.WriteLine("从左上角到右下角的做小路径和为 " + res); // 状态压缩后的动态规划 - Console.WriteLine(minPathSumDPComp(grid)); + res = minPathSumDPComp(grid); + Console.WriteLine("从左上角到右下角的做小路径和为 " + res); } } diff --git a/codes/java/chapter_dynamic_programming/knapsack.java b/codes/java/chapter_dynamic_programming/knapsack.java new file mode 100644 index 000000000..99c62d628 --- /dev/null +++ b/codes/java/chapter_dynamic_programming/knapsack.java @@ -0,0 +1,116 @@ +/** + * File: knapsack.java + * Created Time: 2023-07-10 + * Author: Krahets (krahets@163.com) + */ + +package chapter_dynamic_programming; + +import java.util.Arrays; + +public class knapsack { + + /* 0-1 背包:暴力搜索 */ + static int knapsackDFS(int[] wgt, int[] val, int i, int c) { + // 若已选完所有物品或背包无容量,则返回价值 0 + if (i == 0 || c == 0) { + return 0; + } + // 若超过背包容量,则只能不放入背包 + if (wgt[i - 1] > c) { + return knapsackDFS(wgt, val, i - 1, c); + } + // 计算不放入和放入物品 i 的最大价值 + int no = knapsackDFS(wgt, val, i - 1, c); + int yes = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]; + // 返回两种方案中价值更大的那一个 + return Math.max(no, yes); + } + + /* 0-1 背包:记忆化搜索 */ + static int knapsackDFSMem(int[] wgt, int[] val, int[][] mem, int i, int c) { + // 若已选完所有物品或背包无容量,则返回价值 0 + if (i == 0 || c == 0) { + return 0; + } + // 若已有记录,则直接返回 + if (mem[i][c] != -1) { + return mem[i][c]; + } + // 若超过背包容量,则只能不放入背包 + if (wgt[i - 1] > c) { + return knapsackDFSMem(wgt, val, mem, i - 1, c); + } + // 计算不放入和放入物品 i 的最大价值 + int no = knapsackDFSMem(wgt, val, mem, i - 1, c); + int yes = knapsackDFSMem(wgt, val, mem, i - 1, c - wgt[i - 1]) + val[i - 1]; + // 记录并返回两种方案中价值更大的那一个 + mem[i][c] = Math.max(no, yes); + return mem[i][c]; + } + + /* 0-1 背包:动态规划 */ + static int knapsackDP(int[] wgt, int[] val, int cap) { + int n = wgt.length; + // 初始化 dp 表 + int[][] dp = new int[n + 1][cap + 1]; + // 状态转移 + for (int i = 1; i <= n; i++) { + for (int c = 1; c <= cap; c++) { + if (wgt[i - 1] > c) { + // 若超过背包容量,则不选物品 i + dp[i][c] = dp[i - 1][c]; + } else { + // 不选和选物品 i 这两种方案的较大值 + dp[i][c] = Math.max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[n][cap]; + } + + /* 0-1 背包:状态压缩后的动态规划 */ + static int knapsackDPComp(int[] wgt, int[] val, int cap) { + int n = wgt.length; + // 初始化 dp 表 + int[] dp = new int[cap + 1]; + // 状态转移 + for (int i = 1; i <= n; i++) { + // 倒序遍历 + for (int c = cap; c >= 1; c--) { + if (wgt[i - 1] <= c) { + // 不选和选物品 i 这两种方案的较大值 + dp[c] = Math.max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]); + } + } + } + return dp[cap]; + } + + public static void main(String[] args) { + int[] wgt = { 10, 20, 30, 40, 50 }; + int[] val = { 50, 120, 150, 210, 240 }; + int cap = 50; + int n = wgt.length; + + // 暴力搜索 + int res = knapsackDFS(wgt, val, n, cap); + System.out.println("不超过背包容量的最大物品价值为 " + res); + + // 记忆化搜索 + int[][] mem = new int[n + 1][cap + 1]; + for (int[] row : mem) { + Arrays.fill(row, -1); + } + res = knapsackDFSMem(wgt, val, mem, n, cap); + System.out.println("不超过背包容量的最大物品价值为 " + res); + + // 动态规划 + res = knapsackDP(wgt, val, cap); + System.out.println("不超过背包容量的最大物品价值为 " + res); + + // 状态压缩后的动态规划 + res = knapsackDPComp(wgt, val, cap); + System.out.println("不超过背包容量的最大物品价值为 " + res); + } +} diff --git a/codes/java/chapter_dynamic_programming/min_path_sum.java b/codes/java/chapter_dynamic_programming/min_path_sum.java new file mode 100644 index 000000000..f1636c83f --- /dev/null +++ b/codes/java/chapter_dynamic_programming/min_path_sum.java @@ -0,0 +1,125 @@ +/** + * File: min_path_sum.java + * Created Time: 2023-07-10 + * Author: Krahets (krahets@163.com) + */ + +package chapter_dynamic_programming; + +import java.util.Arrays; + +public class min_path_sum { + /* 最小路径和:暴力搜索 */ + static int minPathSumDFS(int[][] grid, int i, int j) { + // 若为左上角单元格,则终止搜索 + if (i == 0 && j == 0) { + return grid[0][0]; + } + // 若行列索引越界,则返回 +∞ 代价 + if (i < 0 || j < 0) { + return Integer.MAX_VALUE; + } + // 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价 + int left = minPathSumDFS(grid, i - 1, j); + int up = minPathSumDFS(grid, i, j - 1); + // 返回从左上角到 (i, j) 的最小路径代价 + return Math.min(left, up) + grid[i][j]; + } + + /* 最小路径和:记忆化搜索 */ + static int minPathSumDFSMem(int[][] grid, int[][] mem, int i, int j) { + // 若为左上角单元格,则终止搜索 + if (i == 0 && j == 0) { + return grid[0][0]; + } + // 若行列索引越界,则返回 +∞ 代价 + if (i < 0 || j < 0) { + return Integer.MAX_VALUE; + } + // 若已有记录,则直接返回 + if (mem[i][j] != -1) { + return mem[i][j]; + } + // 左边和上边单元格的最小路径代价 + int left = minPathSumDFSMem(grid, mem, i - 1, j); + int up = minPathSumDFSMem(grid, mem, i, j - 1); + // 记录并返回左上角到 (i, j) 的最小路径代价 + mem[i][j] = Math.min(left, up) + grid[i][j]; + return mem[i][j]; + } + + /* 最小路径和:动态规划 */ + static int minPathSumDP(int[][] grid) { + int n = grid.length, m = grid[0].length; + // 初始化 dp 表 + int[][] dp = new int[n][m]; + dp[0][0] = grid[0][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++) { + dp[i][0] = dp[i - 1][0] + grid[i][0]; + } + // 状态转移:其余行列 + for (int i = 1; i < n; i++) { + for (int j = 1; j < m; j++) { + dp[i][j] = Math.min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]; + } + } + return dp[n - 1][m - 1]; + } + + /* 最小路径和:状态压缩后的动态规划 */ + static int minPathSumDPComp(int[][] grid) { + int n = grid.length, m = grid[0].length; + // 初始化 dp 表 + 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++) { + // 状态转移:首列 + dp[0] = 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]; + } + + public static void main(String[] args) { + int[][] grid = { + { 1, 3, 1, 5 }, + { 2, 2, 4, 2 }, + { 5, 3, 2, 1 }, + { 4, 3, 5, 2 } + }; + int n = grid.length, m = grid[0].length; + + // 暴力搜索 + int res = minPathSumDFS(grid, n - 1, m - 1); + System.out.println("从左上角到右下角的做小路径和为 " + res); + + // 记忆化搜索 + int[][] mem = new int[n][m]; + for (int[] row : mem) { + Arrays.fill(row, -1); + } + res = minPathSumDFSMem(grid, mem, n - 1, m - 1); + System.out.println("从左上角到右下角的做小路径和为 " + res); + + // 动态规划 + res = minPathSumDP(grid); + System.out.println("从左上角到右下角的做小路径和为 " + res); + + // 状态压缩后的动态规划 + res = minPathSumDPComp(grid); + System.out.println("从左上角到右下角的做小路径和为 " + res); + } +} diff --git a/codes/python/chapter_dynamic_programming/knapsack.py b/codes/python/chapter_dynamic_programming/knapsack.py index f301eb9db..46ca72b19 100644 --- a/codes/python/chapter_dynamic_programming/knapsack.py +++ b/codes/python/chapter_dynamic_programming/knapsack.py @@ -5,7 +5,7 @@ Author: Krahets (krahets@163.com) """ -def knapsack_dfs(wgt, val, i, c): +def knapsack_dfs(wgt: list[int], val: list[int], i: int, c: int) -> int: """0-1 背包:暴力搜索""" # 若已选完所有物品或背包无容量,则返回价值 0 if i == 0 or c == 0: @@ -20,7 +20,9 @@ def knapsack_dfs(wgt, val, i, c): return max(no, yes) -def knapsack_dfs_mem(wgt, val, mem, i, c): +def knapsack_dfs_mem( + wgt: list[int], val: list[int], mem: list[list[int]], i: int, c: int +) -> int: """0-1 背包:记忆化搜索""" # 若已选完所有物品或背包无容量,则返回价值 0 if i == 0 or c == 0: @@ -39,7 +41,7 @@ def knapsack_dfs_mem(wgt, val, mem, i, c): return mem[i][c] -def knapsack_dp(wgt, val, cap): +def knapsack_dp(wgt: list[int], val: list[int], cap: int) -> int: """0-1 背包:动态规划""" n = len(wgt) # 初始化 dp 表 @@ -56,7 +58,7 @@ def knapsack_dp(wgt, val, cap): return dp[n][cap] -def knapsack_dp_comp(wgt, val, cap): +def knapsack_dp_comp(wgt: list[int], val: list[int], cap: int) -> int: """0-1 背包:状态压缩后的动态规划""" n = len(wgt) # 初始化 dp 表 diff --git a/codes/python/chapter_dynamic_programming/min_path_sum.py b/codes/python/chapter_dynamic_programming/min_path_sum.py index 7f2423d3a..c47ba4431 100644 --- a/codes/python/chapter_dynamic_programming/min_path_sum.py +++ b/codes/python/chapter_dynamic_programming/min_path_sum.py @@ -7,7 +7,7 @@ Author: Krahets (krahets@163.com) from math import inf -def min_path_sum_dfs(grid, i, j): +def min_path_sum_dfs(grid: list[list[int]], i: int, j: int) -> int: """最小路径和:暴力搜索""" # 若为左上角单元格,则终止搜索 if i == 0 and j == 0: @@ -22,7 +22,9 @@ def min_path_sum_dfs(grid, i, j): return min(left, up) + grid[i][j] -def min_path_sum_dfs_mem(grid, mem, i, j): +def min_path_sum_dfs_mem( + grid: list[list[int]], mem: list[list[int]], i: int, j: int +) -> int: """最小路径和:记忆化搜索""" # 若为左上角单元格,则终止搜索 if i == 0 and j == 0: @@ -41,7 +43,7 @@ def min_path_sum_dfs_mem(grid, mem, i, j): return mem[i][j] -def min_path_sum_dp(grid): +def min_path_sum_dp(grid: list[list[int]]) -> int: """最小路径和:动态规划""" n, m = len(grid), len(grid[0]) # 初始化 dp 表 @@ -60,7 +62,7 @@ def min_path_sum_dp(grid): return dp[n - 1][m - 1] -def min_path_sum_dp_comp(grid): +def min_path_sum_dp_comp(grid: list[list[int]]) -> int: """最小路径和:状态压缩后的动态规划""" n, m = len(grid), len(grid[0]) # 初始化 dp 表 @@ -86,17 +88,17 @@ if __name__ == "__main__": # 暴力搜索 res = min_path_sum_dfs(grid, n - 1, m - 1) - print(res) + print(f"从左上角到右下角的做小路径和为 {res}") # 记忆化搜索 mem = [[-1] * m for _ in range(n)] res = min_path_sum_dfs_mem(grid, mem, n - 1, m - 1) - print(res) + print(f"从左上角到右下角的做小路径和为 {res}") # 动态规划 res = min_path_sum_dp(grid) - print(res) + print(f"从左上角到右下角的做小路径和为 {res}") # 状态压缩后的动态规划 res = min_path_sum_dp_comp(grid) - print(res) + print(f"从左上角到右下角的做小路径和为 {res}") diff --git a/docs/chapter_dynamic_programming/dp_solution_pipeline.md b/docs/chapter_dynamic_programming/dp_solution_pipeline.md index 0a7b331e5..13272b32c 100644 --- a/docs/chapter_dynamic_programming/dp_solution_pipeline.md +++ b/docs/chapter_dynamic_programming/dp_solution_pipeline.md @@ -176,7 +176,7 @@ $$ === "Java" ```java title="min_path_sum.java" - [class]{min}-[func]{minPathSumDFSMem} + [class]{min_path_sum}-[func]{minPathSumDFSMem} ``` === "C++"