mirror of
https://github.com/krahets/hello-algo.git
synced 2026-04-24 10:33:34 +08:00
Many bug fixes and improvements (#1270)
* Add Ruby and Kotlin icons Add the avatar of @curtishd * Update README * Synchronize zh-hant and zh versions. * Translate the pythontutor blocks to traditional Chinese * Fix en/mkdocs.yml * Update the landing page of the en version. * Fix the Dockerfile * Refine the en landingpage * Fix en landing page * Reset the README.md
This commit is contained in:
@@ -8,13 +8,14 @@ package chapter_dynamic_programming
|
||||
|
||||
/* 回溯 */
|
||||
fun backtrack(
|
||||
choices: List<Int>,
|
||||
choices: MutableList<Int>,
|
||||
state: Int,
|
||||
n: Int,
|
||||
res: MutableList<Int>
|
||||
) {
|
||||
// 當爬到第 n 階時,方案數量加 1
|
||||
if (state == n) res[0] = res[0] + 1
|
||||
if (state == n)
|
||||
res[0] = res[0] + 1
|
||||
// 走訪所有選擇
|
||||
for (choice in choices) {
|
||||
// 剪枝:不允許越過第 n 階
|
||||
@@ -29,7 +30,7 @@ fun backtrack(
|
||||
fun climbingStairsBacktrack(n: Int): Int {
|
||||
val choices = mutableListOf(1, 2) // 可選擇向上爬 1 階或 2 階
|
||||
val state = 0 // 從第 0 階開始爬
|
||||
val res = ArrayList<Int>()
|
||||
val res = mutableListOf<Int>()
|
||||
res.add(0) // 使用 res[0] 記錄方案數量
|
||||
backtrack(choices, state, n, res)
|
||||
return res[0]
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
import java.util.*
|
||||
|
||||
/* 記憶化搜尋 */
|
||||
fun dfs(i: Int, mem: IntArray): Int {
|
||||
// 已知 dp[1] 和 dp[2] ,返回之
|
||||
@@ -25,7 +23,7 @@ fun dfs(i: Int, mem: IntArray): Int {
|
||||
fun climbingStairsDFSMem(n: Int): Int {
|
||||
// mem[i] 記錄爬到第 i 階的方案總數,-1 代表無記錄
|
||||
val mem = IntArray(n + 1)
|
||||
Arrays.fill(mem, -1)
|
||||
mem.fill(-1)
|
||||
return dfs(n, mem)
|
||||
}
|
||||
|
||||
@@ -33,6 +31,6 @@ fun climbingStairsDFSMem(n: Int): Int {
|
||||
fun main() {
|
||||
val n = 9
|
||||
|
||||
val res: Int = climbingStairsDFSMem(n)
|
||||
val res = climbingStairsDFSMem(n)
|
||||
println("爬 $n 階樓梯共有 $res 種方案")
|
||||
}
|
||||
@@ -27,9 +27,7 @@ fun climbingStairsDPComp(n: Int): Int {
|
||||
var a = 1
|
||||
var b = 2
|
||||
for (i in 3..n) {
|
||||
val tmp = b
|
||||
b += a
|
||||
a = tmp
|
||||
b += a.also { a = b }
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
/* 零錢兌換:動態規劃 */
|
||||
@@ -27,8 +26,7 @@ fun coinChangeDP(coins: IntArray, amt: Int): Int {
|
||||
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()
|
||||
dp[i][a] = min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +39,7 @@ fun coinChangeDPComp(coins: IntArray, amt: Int): Int {
|
||||
val MAX = amt + 1
|
||||
// 初始化 dp 表
|
||||
val dp = IntArray(amt + 1)
|
||||
Arrays.fill(dp, MAX)
|
||||
dp.fill(MAX)
|
||||
dp[0] = 0
|
||||
// 狀態轉移
|
||||
for (i in 1..n) {
|
||||
@@ -51,7 +49,7 @@ fun coinChangeDPComp(coins: IntArray, amt: Int): Int {
|
||||
dp[a] = dp[a]
|
||||
} else {
|
||||
// 不選和選硬幣 i 這兩種方案的較小值
|
||||
dp[a] = min(dp[a].toDouble(), (dp[a - coins[i - 1]] + 1).toDouble()).toInt()
|
||||
dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
/* 編輯距離:暴力搜尋 */
|
||||
@@ -29,7 +28,7 @@ fun editDistanceDFS(
|
||||
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()
|
||||
return min(min(insert, delete), replace) + 1
|
||||
}
|
||||
|
||||
/* 編輯距離:記憶化搜尋 */
|
||||
@@ -55,7 +54,7 @@ fun editDistanceDFSMem(
|
||||
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()
|
||||
mem[i][j] = min(min(insert, delete), replace) + 1
|
||||
return mem[i][j]
|
||||
}
|
||||
|
||||
@@ -79,11 +78,7 @@ fun editDistanceDP(s: String, t: String): Int {
|
||||
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()
|
||||
dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,7 +107,7 @@ fun editDistanceDPComp(s: String, t: String): Int {
|
||||
dp[j] = leftup
|
||||
} else {
|
||||
// 最少編輯步數 = 插入、刪除、替換這三種操作的最少編輯步數 + 1
|
||||
dp[j] = (min(min(dp[j - 1].toDouble(), dp[j].toDouble()), leftup.toDouble()) + 1).toInt()
|
||||
dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1
|
||||
}
|
||||
leftup = temp // 更新為下一輪的 dp[i-1, j-1]
|
||||
}
|
||||
@@ -133,7 +128,8 @@ fun main() {
|
||||
|
||||
// 記憶化搜尋
|
||||
val mem = Array(n + 1) { IntArray(m + 1) }
|
||||
for (row in mem) Arrays.fill(row, -1)
|
||||
for (row in mem)
|
||||
row.fill(-1)
|
||||
res = editDistanceDFSMem(s, t, mem, n, m)
|
||||
println("將 $s 更改為 $t 最少需要編輯 $res 步")
|
||||
|
||||
|
||||
@@ -6,13 +6,12 @@
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
import java.util.*
|
||||
import kotlin.math.max
|
||||
|
||||
/* 0-1 背包:暴力搜尋 */
|
||||
fun knapsackDFS(
|
||||
wgt: IntArray,
|
||||
value: IntArray,
|
||||
_val: IntArray,
|
||||
i: Int,
|
||||
c: Int
|
||||
): Int {
|
||||
@@ -22,19 +21,19 @@ fun knapsackDFS(
|
||||
}
|
||||
// 若超過背包容量,則只能選擇不放入背包
|
||||
if (wgt[i - 1] > c) {
|
||||
return knapsackDFS(wgt, value, i - 1, c)
|
||||
return knapsackDFS(wgt, _val, 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]
|
||||
val no = knapsackDFS(wgt, _val, i - 1, c)
|
||||
val yes = knapsackDFS(wgt, _val, i - 1, c - wgt[i - 1]) + _val[i - 1]
|
||||
// 返回兩種方案中價值更大的那一個
|
||||
return max(no.toDouble(), yes.toDouble()).toInt()
|
||||
return max(no, yes)
|
||||
}
|
||||
|
||||
/* 0-1 背包:記憶化搜尋 */
|
||||
fun knapsackDFSMem(
|
||||
wgt: IntArray,
|
||||
value: IntArray,
|
||||
_val: IntArray,
|
||||
mem: Array<IntArray>,
|
||||
i: Int,
|
||||
c: Int
|
||||
@@ -49,20 +48,20 @@ fun knapsackDFSMem(
|
||||
}
|
||||
// 若超過背包容量,則只能選擇不放入背包
|
||||
if (wgt[i - 1] > c) {
|
||||
return knapsackDFSMem(wgt, value, mem, i - 1, c)
|
||||
return knapsackDFSMem(wgt, _val, 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]
|
||||
val no = knapsackDFSMem(wgt, _val, mem, i - 1, c)
|
||||
val yes = knapsackDFSMem(wgt, _val, mem, i - 1, c - wgt[i - 1]) + _val[i - 1]
|
||||
// 記錄並返回兩種方案中價值更大的那一個
|
||||
mem[i][c] = max(no.toDouble(), yes.toDouble()).toInt()
|
||||
mem[i][c] = max(no, yes)
|
||||
return mem[i][c]
|
||||
}
|
||||
|
||||
/* 0-1 背包:動態規劃 */
|
||||
fun knapsackDP(
|
||||
wgt: IntArray,
|
||||
value: IntArray,
|
||||
_val: IntArray,
|
||||
cap: Int
|
||||
): Int {
|
||||
val n = wgt.size
|
||||
@@ -76,8 +75,7 @@ fun knapsackDP(
|
||||
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()
|
||||
dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + _val[i - 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,7 +85,7 @@ fun knapsackDP(
|
||||
/* 0-1 背包:空間最佳化後的動態規劃 */
|
||||
fun knapsackDPComp(
|
||||
wgt: IntArray,
|
||||
value: IntArray,
|
||||
_val: IntArray,
|
||||
cap: Int
|
||||
): Int {
|
||||
val n = wgt.size
|
||||
@@ -100,7 +98,7 @@ fun knapsackDPComp(
|
||||
if (wgt[i - 1] <= c) {
|
||||
// 不選和選物品 i 這兩種方案的較大值
|
||||
dp[c] =
|
||||
max(dp[c].toDouble(), (dp[c - wgt[i - 1]] + value[i - 1]).toDouble()).toInt()
|
||||
max(dp[c], dp[c - wgt[i - 1]] + _val[i - 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,27 +108,27 @@ fun knapsackDPComp(
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val wgt = intArrayOf(10, 20, 30, 40, 50)
|
||||
val value = intArrayOf(50, 120, 150, 210, 240)
|
||||
val _val = intArrayOf(50, 120, 150, 210, 240)
|
||||
val cap = 50
|
||||
val n = wgt.size
|
||||
|
||||
// 暴力搜尋
|
||||
var res = knapsackDFS(wgt, value, n, cap)
|
||||
var res = knapsackDFS(wgt, _val, n, cap)
|
||||
println("不超過背包容量的最大物品價值為 $res")
|
||||
|
||||
// 記憶化搜尋
|
||||
val mem = Array(n + 1) { IntArray(cap + 1) }
|
||||
for (row in mem) {
|
||||
Arrays.fill(row, -1)
|
||||
row.fill(-1)
|
||||
}
|
||||
res = knapsackDFSMem(wgt, value, mem, n, cap)
|
||||
res = knapsackDFSMem(wgt, _val, mem, n, cap)
|
||||
println("不超過背包容量的最大物品價值為 $res")
|
||||
|
||||
// 動態規劃
|
||||
res = knapsackDP(wgt, value, cap)
|
||||
res = knapsackDP(wgt, _val, cap)
|
||||
println("不超過背包容量的最大物品價值為 $res")
|
||||
|
||||
// 空間最佳化後的動態規劃
|
||||
res = knapsackDPComp(wgt, value, cap)
|
||||
res = knapsackDPComp(wgt, _val, cap)
|
||||
println("不超過背包容量的最大物品價值為 $res")
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ fun minCostClimbingStairsDP(cost: IntArray): Int {
|
||||
dp[2] = cost[2]
|
||||
// 狀態轉移:從較小子問題逐步求解較大子問題
|
||||
for (i in 3..n) {
|
||||
dp[i] = (min(dp[i - 1].toDouble(), dp[i - 2].toDouble()) + cost[i]).toInt()
|
||||
dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]
|
||||
}
|
||||
return dp[n]
|
||||
}
|
||||
@@ -32,7 +32,7 @@ fun minCostClimbingStairsDPComp(cost: IntArray): Int {
|
||||
var b = cost[2]
|
||||
for (i in 3..n) {
|
||||
val tmp = b
|
||||
b = (min(a.toDouble(), tmp.toDouble()) + cost[i]).toInt()
|
||||
b = min(a, tmp) + cost[i]
|
||||
a = tmp
|
||||
}
|
||||
return b
|
||||
|
||||
@@ -6,15 +6,10 @@
|
||||
|
||||
package chapter_dynamic_programming
|
||||
|
||||
import java.util.*
|
||||
import kotlin.math.min
|
||||
|
||||
/* 最小路徑和:暴力搜尋 */
|
||||
fun minPathSumDFS(
|
||||
grid: Array<Array<Int>>,
|
||||
i: Int,
|
||||
j: Int
|
||||
): Int {
|
||||
fun minPathSumDFS(grid: Array<IntArray>, i: Int, j: Int): Int {
|
||||
// 若為左上角單元格,則終止搜尋
|
||||
if (i == 0 && j == 0) {
|
||||
return grid[0][0]
|
||||
@@ -27,13 +22,13 @@ fun minPathSumDFS(
|
||||
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()
|
||||
return min(left, up) + grid[i][j]
|
||||
}
|
||||
|
||||
/* 最小路徑和:記憶化搜尋 */
|
||||
fun minPathSumDFSMem(
|
||||
grid: Array<Array<Int>>,
|
||||
mem: Array<Array<Int>>,
|
||||
grid: Array<IntArray>,
|
||||
mem: Array<IntArray>,
|
||||
i: Int,
|
||||
j: Int
|
||||
): Int {
|
||||
@@ -53,12 +48,12 @@ fun minPathSumDFSMem(
|
||||
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()
|
||||
mem[i][j] = min(left, up) + grid[i][j]
|
||||
return mem[i][j]
|
||||
}
|
||||
|
||||
/* 最小路徑和:動態規劃 */
|
||||
fun minPathSumDP(grid: Array<Array<Int>>): Int {
|
||||
fun minPathSumDP(grid: Array<IntArray>): Int {
|
||||
val n = grid.size
|
||||
val m = grid[0].size
|
||||
// 初始化 dp 表
|
||||
@@ -75,15 +70,14 @@ fun minPathSumDP(grid: Array<Array<Int>>): Int {
|
||||
// 狀態轉移:其餘行和列
|
||||
for (i in 1..<n) {
|
||||
for (j in 1..<m) {
|
||||
dp[i][j] =
|
||||
(min(dp[i][j - 1].toDouble(), dp[i - 1][j].toDouble()) + grid[i][j]).toInt()
|
||||
dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]
|
||||
}
|
||||
}
|
||||
return dp[n - 1][m - 1]
|
||||
}
|
||||
|
||||
/* 最小路徑和:空間最佳化後的動態規劃 */
|
||||
fun minPathSumDPComp(grid: Array<Array<Int>>): Int {
|
||||
fun minPathSumDPComp(grid: Array<IntArray>): Int {
|
||||
val n = grid.size
|
||||
val m = grid[0].size
|
||||
// 初始化 dp 表
|
||||
@@ -99,7 +93,7 @@ fun minPathSumDPComp(grid: Array<Array<Int>>): Int {
|
||||
dp[0] = dp[0] + grid[i][0]
|
||||
// 狀態轉移:其餘列
|
||||
for (j in 1..<m) {
|
||||
dp[j] = (min(dp[j - 1].toDouble(), dp[j].toDouble()) + grid[i][j]).toInt()
|
||||
dp[j] = min(dp[j - 1], dp[j]) + grid[i][j]
|
||||
}
|
||||
}
|
||||
return dp[m - 1]
|
||||
@@ -108,10 +102,10 @@ fun minPathSumDPComp(grid: Array<Array<Int>>): Int {
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val grid = arrayOf(
|
||||
arrayOf(1, 3, 1, 5),
|
||||
arrayOf(2, 2, 4, 2),
|
||||
arrayOf(5, 3, 2, 1),
|
||||
arrayOf(4, 3, 5, 2)
|
||||
intArrayOf(1, 3, 1, 5),
|
||||
intArrayOf(2, 2, 4, 2),
|
||||
intArrayOf(5, 3, 2, 1),
|
||||
intArrayOf(4, 3, 5, 2)
|
||||
)
|
||||
val n = grid.size
|
||||
val m = grid[0].size
|
||||
@@ -121,9 +115,9 @@ fun main() {
|
||||
println("從左上角到右下角的最小路徑和為 $res")
|
||||
|
||||
// 記憶化搜尋
|
||||
val mem = Array(n) { Array(m) { 0 } }
|
||||
val mem = Array(n) { IntArray(m) }
|
||||
for (row in mem) {
|
||||
Arrays.fill(row, -1)
|
||||
row.fill(-1)
|
||||
}
|
||||
res = minPathSumDFSMem(grid, mem, n - 1, m - 1)
|
||||
println("從左上角到右下角的最小路徑和為 $res")
|
||||
|
||||
@@ -9,11 +9,7 @@ package chapter_dynamic_programming
|
||||
import kotlin.math.max
|
||||
|
||||
/* 完全背包:動態規劃 */
|
||||
fun unboundedKnapsackDP(
|
||||
wgt: IntArray,
|
||||
value: IntArray,
|
||||
cap: Int
|
||||
): Int {
|
||||
fun unboundedKnapsackDP(wgt: IntArray, _val: IntArray, cap: Int): Int {
|
||||
val n = wgt.size
|
||||
// 初始化 dp 表
|
||||
val dp = Array(n + 1) { IntArray(cap + 1) }
|
||||
@@ -25,8 +21,7 @@ fun unboundedKnapsackDP(
|
||||
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()
|
||||
dp[i][c] = max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + _val[i - 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,7 +31,7 @@ fun unboundedKnapsackDP(
|
||||
/* 完全背包:空間最佳化後的動態規劃 */
|
||||
fun unboundedKnapsackDPComp(
|
||||
wgt: IntArray,
|
||||
value: IntArray,
|
||||
_val: IntArray,
|
||||
cap: Int
|
||||
): Int {
|
||||
val n = wgt.size
|
||||
@@ -50,8 +45,7 @@ fun unboundedKnapsackDPComp(
|
||||
dp[c] = dp[c]
|
||||
} else {
|
||||
// 不選和選物品 i 這兩種方案的較大值
|
||||
dp[c] =
|
||||
max(dp[c].toDouble(), (dp[c - wgt[i - 1]] + value[i - 1]).toDouble()).toInt()
|
||||
dp[c] = max(dp[c], dp[c - wgt[i - 1]] + _val[i - 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,14 +55,14 @@ fun unboundedKnapsackDPComp(
|
||||
/* Driver Code */
|
||||
fun main() {
|
||||
val wgt = intArrayOf(1, 2, 3)
|
||||
val value = intArrayOf(5, 11, 15)
|
||||
val _val = intArrayOf(5, 11, 15)
|
||||
val cap = 4
|
||||
|
||||
// 動態規劃
|
||||
var res = unboundedKnapsackDP(wgt, value, cap)
|
||||
var res = unboundedKnapsackDP(wgt, _val, cap)
|
||||
println("不超過背包容量的最大物品價值為 $res")
|
||||
|
||||
// 空間最佳化後的動態規劃
|
||||
res = unboundedKnapsackDPComp(wgt, value, cap)
|
||||
res = unboundedKnapsackDPComp(wgt, _val, cap)
|
||||
println("不超過背包容量的最大物品價值為 $res")
|
||||
}
|
||||
Reference in New Issue
Block a user