diff --git a/codes/kotlin/chapter_dynamic_programming/climbing_stairs_backtrack.kt b/codes/kotlin/chapter_dynamic_programming/climbing_stairs_backtrack.kt new file mode 100644 index 000000000..03af6060e --- /dev/null +++ b/codes/kotlin/chapter_dynamic_programming/climbing_stairs_backtrack.kt @@ -0,0 +1,44 @@ +/** + * File: climbing_stairs_backtrack.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +/* 回溯 */ +fun backtrack( + choices: List, + state: Int, + n: Int, + res: MutableList +) { + // 当爬到第 n 阶时,方案数量加 1 + if (state == n) res[0] = res[0] + 1 + // 遍历所有选择 + for (choice in choices) { + // 剪枝:不允许越过第 n 阶 + if (state + choice > n) continue + // 尝试:做出选择,更新状态 + backtrack(choices, state + choice, n, res) + // 回退 + } +} + +/* 爬楼梯:回溯 */ +fun climbingStairsBacktrack(n: Int): Int { + val choices = mutableListOf(1, 2) // 可选择向上爬 1 阶或 2 阶 + val state = 0 // 从第 0 阶开始爬 + val res = ArrayList() + res.add(0) // 使用 res[0] 记录方案数量 + backtrack(choices, state, n, res) + return res[0] +} + +/* Driver Code */ +fun main() { + val n = 9 + + val res = climbingStairsBacktrack(n) + println("爬 $n 阶楼梯共有 $res 种方案") +} \ No newline at end of file diff --git a/codes/kotlin/chapter_dynamic_programming/climbing_stairs_constraint_dp.kt b/codes/kotlin/chapter_dynamic_programming/climbing_stairs_constraint_dp.kt new file mode 100644 index 000000000..4dc70a151 --- /dev/null +++ b/codes/kotlin/chapter_dynamic_programming/climbing_stairs_constraint_dp.kt @@ -0,0 +1,35 @@ +/** + * File: climbing_stairs_constraint_dp.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +/* 带约束爬楼梯:动态规划 */ +fun climbingStairsConstraintDP(n: Int): Int { + if (n == 1 || n == 2) { + return 1 + } + // 初始化 dp 表,用于存储子问题的解 + val dp = Array(n + 1) { IntArray(3) } + // 初始状态:预设最小子问题的解 + dp[1][1] = 1 + dp[1][2] = 0 + dp[2][1] = 0 + dp[2][2] = 1 + // 状态转移:从较小子问题逐步求解较大子问题 + for (i in 3..n) { + dp[i][1] = dp[i - 1][2] + dp[i][2] = dp[i - 2][1] + dp[i - 2][2] + } + return dp[n][1] + dp[n][2] +} + +/* Driver Code */ +fun main() { + val n = 9 + + val res = climbingStairsConstraintDP(n) + println("爬 $n 阶楼梯共有 $res 种方案") +} \ No newline at end of file diff --git a/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dfs.kt b/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dfs.kt new file mode 100644 index 000000000..ecb2345ad --- /dev/null +++ b/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dfs.kt @@ -0,0 +1,29 @@ +/** + * File: climbing_stairs_dfs.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +/* 搜索 */ +fun dfs(i: Int): Int { + // 已知 dp[1] 和 dp[2] ,返回之 + if (i == 1 || i == 2) return i + // dp[i] = dp[i-1] + dp[i-2] + val count = dfs(i - 1) + dfs(i - 2) + return count +} + +/* 爬楼梯:搜索 */ +fun climbingStairsDFS(n: Int): Int { + return dfs(n) +} + +/* Driver Code */ +fun main() { + val n = 9 + + val res = climbingStairsDFS(n) + println("爬 $n 阶楼梯共有 $res 种方案") +} \ No newline at end of file diff --git a/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dp.kt b/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dp.kt new file mode 100644 index 000000000..ed88c0bd0 --- /dev/null +++ b/codes/kotlin/chapter_dynamic_programming/climbing_stairs_dp.kt @@ -0,0 +1,46 @@ +/** + * File: climbing_stairs_dp.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +/* 爬楼梯:动态规划 */ +fun climbingStairsDP(n: Int): Int { + if (n == 1 || n == 2) return n + // 初始化 dp 表,用于存储子问题的解 + val dp = IntArray(n + 1) + // 初始状态:预设最小子问题的解 + dp[1] = 1 + dp[2] = 2 + // 状态转移:从较小子问题逐步求解较大子问题 + for (i in 3..n) { + dp[i] = dp[i - 1] + dp[i - 2] + } + return dp[n] +} + +/* 爬楼梯:空间优化后的动态规划 */ +fun climbingStairsDPComp(n: Int): Int { + if (n == 1 || n == 2) return n + var a = 1 + var b = 2 + for (i in 3..n) { + val tmp = b + b += a + a = tmp + } + return b +} + +/* Driver Code */ +fun main() { + val n = 9 + + var res = climbingStairsDP(n) + println("爬 $n 阶楼梯共有 $res 种方案") + + res = climbingStairsDPComp(n) + println("爬 $n 阶楼梯共有 $res 种方案") +} \ No newline at end of file diff --git a/codes/kotlin/chapter_dynamic_programming/climbing_stiars_dfs_mem.kt b/codes/kotlin/chapter_dynamic_programming/climbing_stiars_dfs_mem.kt new file mode 100644 index 000000000..d6c20aa18 --- /dev/null +++ b/codes/kotlin/chapter_dynamic_programming/climbing_stiars_dfs_mem.kt @@ -0,0 +1,38 @@ +/** + * File: climbing_stairs_dfs_mem.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +import java.util.* + +/* 记忆化搜索 */ +fun dfs(i: Int, mem: IntArray): Int { + // 已知 dp[1] 和 dp[2] ,返回之 + if (i == 1 || i == 2) return i + // 若存在记录 dp[i] ,则直接返回之 + if (mem[i] != -1) return mem[i] + // dp[i] = dp[i-1] + dp[i-2] + val count = dfs(i - 1, mem) + dfs(i - 2, mem) + // 记录 dp[i] + mem[i] = count + return count +} + +/* 爬楼梯:记忆化搜索 */ +fun climbingStairsDFSMem(n: Int): Int { + // mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录 + val mem = IntArray(n + 1) + Arrays.fill(mem, -1) + return dfs(n, mem) +} + +/* Driver Code */ +fun main() { + val n = 9 + + val res: Int = climbingStairsDFSMem(n) + println("爬 $n 阶楼梯共有 $res 种方案") +} \ No newline at end of file diff --git a/codes/kotlin/chapter_dynamic_programming/coin_change.kt b/codes/kotlin/chapter_dynamic_programming/coin_change.kt new file mode 100644 index 000000000..903dacc29 --- /dev/null +++ b/codes/kotlin/chapter_dynamic_programming/coin_change.kt @@ -0,0 +1,73 @@ +/** + * File: coin_change.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +import java.util.* +import kotlin.math.min + +/* 零钱兑换:动态规划 */ +fun coinChangeDP(coins: IntArray, amt: Int): Int { + val n = coins.size + val MAX = amt + 1 + // 初始化 dp 表 + val dp = Array(n + 1) { IntArray(amt + 1) } + // 状态转移:首行首列 + for (a in 1..amt) { + dp[0][a] = MAX + } + // 状态转移:其余行和列 + for (i in 1..n) { + for (a in 1..amt) { + if (coins[i - 1] > a) { + // 若超过目标金额,则不选硬币 i + dp[i][a] = dp[i - 1][a] + } else { + // 不选和选硬币 i 这两种方案的较小值 + dp[i][a] = min(dp[i - 1][a].toDouble(), (dp[i][a - coins[i - 1]] + 1).toDouble()) + .toInt() + } + } + } + return if (dp[n][amt] != MAX) dp[n][amt] else -1 +} + +/* 零钱兑换:空间优化后的动态规划 */ +fun coinChangeDPComp(coins: IntArray, amt: Int): Int { + val n = coins.size + val MAX = amt + 1 + // 初始化 dp 表 + val dp = IntArray(amt + 1) + Arrays.fill(dp, MAX) + dp[0] = 0 + // 状态转移 + for (i in 1..n) { + for (a in 1..amt) { + if (coins[i - 1] > a) { + // 若超过目标金额,则不选硬币 i + dp[a] = dp[a] + } else { + // 不选和选硬币 i 这两种方案的较小值 + dp[a] = min(dp[a].toDouble(), (dp[a - coins[i - 1]] + 1).toDouble()).toInt() + } + } + } + return if (dp[amt] != MAX) dp[amt] else -1 +} + +/* Driver Code */ +fun main() { + val coins = intArrayOf(1, 2, 5) + val amt = 4 + + // 动态规划 + var res = coinChangeDP(coins, amt) + println("凑到目标金额所需的最少硬币数量为 $res") + + // 空间优化后的动态规划 + res = coinChangeDPComp(coins, amt) + println("凑到目标金额所需的最少硬币数量为 $res") +} \ No newline at end of file diff --git a/codes/kotlin/chapter_dynamic_programming/coin_change_ii.kt b/codes/kotlin/chapter_dynamic_programming/coin_change_ii.kt new file mode 100644 index 000000000..683a86055 --- /dev/null +++ b/codes/kotlin/chapter_dynamic_programming/coin_change_ii.kt @@ -0,0 +1,66 @@ +/** + * File: coin_change_ii.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +/* 零钱兑换 II:动态规划 */ +fun coinChangeIIDP(coins: IntArray, amt: Int): Int { + val n = coins.size + // 初始化 dp 表 + val dp = Array(n + 1) { IntArray(amt + 1) } + // 初始化首列 + for (i in 0..n) { + dp[i][0] = 1 + } + // 状态转移 + for (i in 1..n) { + for (a in 1..amt) { + if (coins[i - 1] > a) { + // 若超过目标金额,则不选硬币 i + dp[i][a] = dp[i - 1][a] + } else { + // 不选和选硬币 i 这两种方案之和 + dp[i][a] = dp[i - 1][a] + dp[i][a - coins[i - 1]] + } + } + } + return dp[n][amt] +} + +/* 零钱兑换 II:空间优化后的动态规划 */ +fun coinChangeIIDPComp(coins: IntArray, amt: Int): Int { + val n = coins.size + // 初始化 dp 表 + val dp = IntArray(amt + 1) + dp[0] = 1 + // 状态转移 + for (i in 1..n) { + for (a in 1..amt) { + if (coins[i - 1] > a) { + // 若超过目标金额,则不选硬币 i + dp[a] = dp[a] + } else { + // 不选和选硬币 i 这两种方案之和 + dp[a] = dp[a] + dp[a - coins[i - 1]] + } + } + } + return dp[amt] +} + +/* Driver Code */ +fun main() { + val coins = intArrayOf(1, 2, 5) + val amt = 5 + + // 动态规划 + var res = coinChangeIIDP(coins, amt) + println("凑出目标金额的硬币组合数量为 $res") + + // 空间优化后的动态规划 + res = coinChangeIIDPComp(coins, amt) + println("凑出目标金额的硬币组合数量为 $res") +} \ No newline at end of file diff --git a/codes/kotlin/chapter_dynamic_programming/edit_distance.kt b/codes/kotlin/chapter_dynamic_programming/edit_distance.kt new file mode 100644 index 000000000..eb80a49a8 --- /dev/null +++ b/codes/kotlin/chapter_dynamic_programming/edit_distance.kt @@ -0,0 +1,147 @@ +/** + * File: edit_distance.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +import java.util.* +import kotlin.math.min + +/* 编辑距离:暴力搜索 */ +fun editDistanceDFS( + s: String, + t: String, + i: Int, + j: Int +): Int { + // 若 s 和 t 都为空,则返回 0 + if (i == 0 && j == 0) return 0 + // 若 s 为空,则返回 t 长度 + if (i == 0) return j + // 若 t 为空,则返回 s 长度 + if (j == 0) return i + // 若两字符相等,则直接跳过此两字符 + if (s[i - 1] == t[j - 1]) return editDistanceDFS(s, t, i - 1, j - 1) + // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 + val insert = editDistanceDFS(s, t, i, j - 1) + val delete = editDistanceDFS(s, t, i - 1, j) + val replace = editDistanceDFS(s, t, i - 1, j - 1) + // 返回最少编辑步数 + return (min(min(insert.toDouble(), delete.toDouble()), replace.toDouble()) + 1).toInt() +} + +/* 编辑距离:记忆化搜索 */ +fun editDistanceDFSMem( + s: String, + t: String, + mem: Array, + i: Int, + j: Int +): Int { + // 若 s 和 t 都为空,则返回 0 + if (i == 0 && j == 0) return 0 + // 若 s 为空,则返回 t 长度 + if (i == 0) return j + // 若 t 为空,则返回 s 长度 + if (j == 0) return i + // 若已有记录,则直接返回之 + if (mem[i][j] != -1) return mem[i][j] + // 若两字符相等,则直接跳过此两字符 + if (s[i - 1] == t[j - 1]) return editDistanceDFSMem(s, t, mem, i - 1, j - 1) + // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 + val insert = editDistanceDFSMem(s, t, mem, i, j - 1) + val delete = editDistanceDFSMem(s, t, mem, i - 1, j) + val replace = editDistanceDFSMem(s, t, mem, i - 1, j - 1) + // 记录并返回最少编辑步数 + mem[i][j] = (min(min(insert.toDouble(), delete.toDouble()), replace.toDouble()) + 1).toInt() + return mem[i][j] +} + +/* 编辑距离:动态规划 */ +fun editDistanceDP(s: String, t: String): Int { + val n = s.length + val m = t.length + val dp = Array(n + 1) { IntArray(m + 1) } + // 状态转移:首行首列 + for (i in 1..n) { + dp[i][0] = i + } + for (j in 1..m) { + dp[0][j] = j + } + // 状态转移:其余行和列 + for (i in 1..n) { + for (j in 1..m) { + if (s[i - 1] == t[j - 1]) { + // 若两字符相等,则直接跳过此两字符 + dp[i][j] = dp[i - 1][j - 1] + } else { + // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 + dp[i][j] = + (min( + min(dp[i][j - 1].toDouble(), dp[i - 1][j].toDouble()), + dp[i - 1][j - 1].toDouble() + ) + 1).toInt() + } + } + } + return dp[n][m] +} + +/* 编辑距离:空间优化后的动态规划 */ +fun editDistanceDPComp(s: String, t: String): Int { + val n = s.length + val m = t.length + val dp = IntArray(m + 1) + // 状态转移:首行 + for (j in 1..m) { + dp[j] = j + } + // 状态转移:其余行 + for (i in 1..n) { + // 状态转移:首列 + var leftup = dp[0] // 暂存 dp[i-1, j-1] + dp[0] = i + // 状态转移:其余列 + for (j in 1..m) { + val temp = dp[j] + if (s[i - 1] == t[j - 1]) { + // 若两字符相等,则直接跳过此两字符 + dp[j] = leftup + } else { + // 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1 + dp[j] = (min(min(dp[j - 1].toDouble(), dp[j].toDouble()), leftup.toDouble()) + 1).toInt() + } + leftup = temp // 更新为下一轮的 dp[i-1, j-1] + } + } + return dp[m] +} + +/* Driver Code */ +fun main() { + val s = "bag" + val t = "pack" + val n = s.length + val m = t.length + + // 暴力搜索 + var res = editDistanceDFS(s, t, n, m) + println("将 $s 更改为 $t 最少需要编辑 $res 步") + + // 记忆化搜索 + val mem = Array(n + 1) { IntArray(m + 1) } + for (row in mem) Arrays.fill(row, -1) + res = editDistanceDFSMem(s, t, mem, n, m) + println("将 $s 更改为 $t 最少需要编辑 $res 步") + + // 动态规划 + res = editDistanceDP(s, t) + println("将 $s 更改为 $t 最少需要编辑 $res 步") + + // 空间优化后的动态规划 + res = editDistanceDPComp(s, t) + println("将 $s 更改为 $t 最少需要编辑 $res 步") +} \ No newline at end of file diff --git a/codes/kotlin/chapter_dynamic_programming/knapsack.kt b/codes/kotlin/chapter_dynamic_programming/knapsack.kt new file mode 100644 index 000000000..d1ef7f771 --- /dev/null +++ b/codes/kotlin/chapter_dynamic_programming/knapsack.kt @@ -0,0 +1,136 @@ +/** + * File: knapsack.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +import java.util.* +import kotlin.math.max + +/* 0-1 背包:暴力搜索 */ +fun knapsackDFS( + wgt: IntArray, + value: IntArray, + i: Int, + c: Int +): Int { + // 若已选完所有物品或背包无剩余容量,则返回价值 0 + if (i == 0 || c == 0) { + return 0 + } + // 若超过背包容量,则只能选择不放入背包 + if (wgt[i - 1] > c) { + return knapsackDFS(wgt, value, i - 1, c) + } + // 计算不放入和放入物品 i 的最大价值 + val no = knapsackDFS(wgt, value, i - 1, c) + val yes = knapsackDFS(wgt, value, i - 1, c - wgt[i - 1]) + value[i - 1] + // 返回两种方案中价值更大的那一个 + return max(no.toDouble(), yes.toDouble()).toInt() +} + +/* 0-1 背包:记忆化搜索 */ +fun knapsackDFSMem( + wgt: IntArray, + value: IntArray, + mem: Array, + i: Int, + c: Int +): Int { + // 若已选完所有物品或背包无剩余容量,则返回价值 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, value, mem, i - 1, c) + } + // 计算不放入和放入物品 i 的最大价值 + val no = knapsackDFSMem(wgt, value, mem, i - 1, c) + val yes = knapsackDFSMem(wgt, value, mem, i - 1, c - wgt[i - 1]) + value[i - 1] + // 记录并返回两种方案中价值更大的那一个 + mem[i][c] = max(no.toDouble(), yes.toDouble()).toInt() + return mem[i][c] +} + +/* 0-1 背包:动态规划 */ +fun knapsackDP( + wgt: IntArray, + value: IntArray, + cap: Int +): Int { + val n = wgt.size + // 初始化 dp 表 + val dp = Array(n + 1) { IntArray(cap + 1) } + // 状态转移 + for (i in 1..n) { + for (c in 1..cap) { + if (wgt[i - 1] > c) { + // 若超过背包容量,则不选物品 i + dp[i][c] = dp[i - 1][c] + } else { + // 不选和选物品 i 这两种方案的较大值 + dp[i][c] = max(dp[i - 1][c].toDouble(), (dp[i - 1][c - wgt[i - 1]] + value[i - 1]).toDouble()) + .toInt() + } + } + } + return dp[n][cap] +} + +/* 0-1 背包:空间优化后的动态规划 */ +fun knapsackDPComp( + wgt: IntArray, + value: IntArray, + cap: Int +): Int { + val n = wgt.size + // 初始化 dp 表 + val dp = IntArray(cap + 1) + // 状态转移 + for (i in 1..n) { + // 倒序遍历 + for (c in cap downTo 1) { + if (wgt[i - 1] <= c) { + // 不选和选物品 i 这两种方案的较大值 + dp[c] = + max(dp[c].toDouble(), (dp[c - wgt[i - 1]] + value[i - 1]).toDouble()).toInt() + } + } + } + return dp[cap] +} + +/* Driver Code */ +fun main() { + val wgt = intArrayOf(10, 20, 30, 40, 50) + val value = intArrayOf(50, 120, 150, 210, 240) + val cap = 50 + val n = wgt.size + + // 暴力搜索 + var res = knapsackDFS(wgt, value, n, cap) + println("不超过背包容量的最大物品价值为 $res") + + // 记忆化搜索 + val mem = Array(n + 1) { IntArray(cap + 1) } + for (row in mem) { + Arrays.fill(row, -1) + } + res = knapsackDFSMem(wgt, value, mem, n, cap) + println("不超过背包容量的最大物品价值为 $res") + + // 动态规划 + res = knapsackDP(wgt, value, cap) + println("不超过背包容量的最大物品价值为 $res") + + // 空间优化后的动态规划 + res = knapsackDPComp(wgt, value, cap) + println("不超过背包容量的最大物品价值为 $res") +} diff --git a/codes/kotlin/chapter_dynamic_programming/min_cost_climbing_stairs_dp.kt b/codes/kotlin/chapter_dynamic_programming/min_cost_climbing_stairs_dp.kt new file mode 100644 index 000000000..74df32584 --- /dev/null +++ b/codes/kotlin/chapter_dynamic_programming/min_cost_climbing_stairs_dp.kt @@ -0,0 +1,51 @@ +/** + * File: min_cost_climbing_stairs_dp.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +import kotlin.math.min + +/* 爬楼梯最小代价:动态规划 */ +fun minCostClimbingStairsDP(cost: IntArray): Int { + val n = cost.size - 1 + if (n == 1 || n == 2) return cost[n] + // 初始化 dp 表,用于存储子问题的解 + val dp = IntArray(n + 1) + // 初始状态:预设最小子问题的解 + dp[1] = cost[1] + dp[2] = cost[2] + // 状态转移:从较小子问题逐步求解较大子问题 + for (i in 3..n) { + dp[i] = (min(dp[i - 1].toDouble(), dp[i - 2].toDouble()) + cost[i]).toInt() + } + return dp[n] +} + +/* 爬楼梯最小代价:空间优化后的动态规划 */ +fun minCostClimbingStairsDPComp(cost: IntArray): Int { + val n = cost.size - 1 + if (n == 1 || n == 2) return cost[n] + var a = cost[1] + var b = cost[2] + for (i in 3..n) { + val tmp = b + b = (min(a.toDouble(), tmp.toDouble()) + cost[i]).toInt() + a = tmp + } + return b +} + +/* Driver Code */ +fun main() { + val cost = intArrayOf(0, 1, 10, 1, 1, 1, 10, 1, 1, 10, 1) + println("输入楼梯的代价列表为 ${cost.contentToString()}") + + var res = minCostClimbingStairsDP(cost) + println("爬完楼梯的最低代价为 $res") + + res = minCostClimbingStairsDPComp(cost) + println("爬完楼梯的最低代价为 $res") +} \ No newline at end of file diff --git a/codes/kotlin/chapter_dynamic_programming/min_path_sum.kt b/codes/kotlin/chapter_dynamic_programming/min_path_sum.kt new file mode 100644 index 000000000..1d86072d1 --- /dev/null +++ b/codes/kotlin/chapter_dynamic_programming/min_path_sum.kt @@ -0,0 +1,138 @@ +/** + * File: min_path_sum.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_dynamic_programming + +import java.util.* +import kotlin.math.min + +/* 最小路径和:暴力搜索 */ +fun minPathSumDFS( + grid: Array>, + i: Int, + j: Int +): Int { + // 若为左上角单元格,则终止搜索 + if (i == 0 && j == 0) { + return grid[0][0] + } + // 若行列索引越界,则返回 +∞ 代价 + if (i < 0 || j < 0) { + return Int.MAX_VALUE + } + // 计算从左上角到 (i-1, j) 和 (i, j-1) 的最小路径代价 + val up = minPathSumDFS(grid, i - 1, j) + val left = minPathSumDFS(grid, i, j - 1) + // 返回从左上角到 (i, j) 的最小路径代价 + return (min(left.toDouble(), up.toDouble()) + grid[i][j]).toInt() +} + +/* 最小路径和:记忆化搜索 */ +fun minPathSumDFSMem( + grid: Array>, + mem: Array>, + i: Int, + j: Int +): Int { + // 若为左上角单元格,则终止搜索 + if (i == 0 && j == 0) { + return grid[0][0] + } + // 若行列索引越界,则返回 +∞ 代价 + if (i < 0 || j < 0) { + return Int.MAX_VALUE + } + // 若已有记录,则直接返回 + if (mem[i][j] != -1) { + return mem[i][j] + } + // 左边和上边单元格的最小路径代价 + val up = minPathSumDFSMem(grid, mem, i - 1, j) + val left = minPathSumDFSMem(grid, mem, i, j - 1) + // 记录并返回左上角到 (i, j) 的最小路径代价 + mem[i][j] = (min(left.toDouble(), up.toDouble()) + grid[i][j]).toInt() + return mem[i][j] +} + +/* 最小路径和:动态规划 */ +fun minPathSumDP(grid: Array>): Int { + val n = grid.size + val m = grid[0].size + // 初始化 dp 表 + val dp = Array(n) { IntArray(m) } + dp[0][0] = grid[0][0] + // 状态转移:首行 + for (j in 1..>): Int { + val n = grid.size + val m = grid[0].size + // 初始化 dp 表 + val dp = IntArray(m) + // 状态转移:首行 + dp[0] = grid[0][0] + for (j in 1.. c) { + // 若超过背包容量,则不选物品 i + dp[i][c] = dp[i - 1][c] + } else { + // 不选和选物品 i 这两种方案的较大值 + dp[i][c] = max(dp[i - 1][c].toDouble(), (dp[i][c - wgt[i - 1]] + value[i - 1]).toDouble()) + .toInt() + } + } + } + return dp[n][cap] +} + +/* 完全背包:空间优化后的动态规划 */ +fun unboundedKnapsackDPComp( + wgt: IntArray, + value: IntArray, + cap: Int +): Int { + val n = wgt.size + // 初始化 dp 表 + val dp = IntArray(cap + 1) + // 状态转移 + for (i in 1..n) { + for (c in 1..cap) { + if (wgt[i - 1] > c) { + // 若超过背包容量,则不选物品 i + dp[c] = dp[c] + } else { + // 不选和选物品 i 这两种方案的较大值 + dp[c] = + max(dp[c].toDouble(), (dp[c - wgt[i - 1]] + value[i - 1]).toDouble()).toInt() + } + } + } + return dp[cap] +} + +/* Driver Code */ +fun main() { + val wgt = intArrayOf(1, 2, 3) + val value = intArrayOf(5, 11, 15) + val cap = 4 + + // 动态规划 + var res = unboundedKnapsackDP(wgt, value, cap) + println("不超过背包容量的最大物品价值为 $res") + + // 空间优化后的动态规划 + res = unboundedKnapsackDPComp(wgt, value, cap) + println("不超过背包容量的最大物品价值为 $res") +} \ No newline at end of file