Skip to content

Commit

Permalink
[feat] add ruby code - chapter dynamic programming (#1378)
Browse files Browse the repository at this point in the history
  • Loading branch information
khoaxuantu committed May 30, 2024
1 parent 63bcdb7 commit a14be17
Show file tree
Hide file tree
Showing 12 changed files with 702 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
=begin
File: climbing_stairs_backtrack.rb
Created Time: 2024-05-29
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
=end

### 回溯 ###
def backtrack(choices, state, n, res)
# 当爬到第 n 阶时,方案数量加 1
res[0] += 1 if state == n
# 遍历所有选择
for choice in choices
# 剪枝:不允许越过第 n 阶
next if state + choice > n

# 尝试:做出选择,更新状态
backtrack(choices, state + choice, n, res)
end
# 回退
end

### 爬楼梯:回溯 ###
def climbing_stairs_backtrack(n)
choices = [1, 2] # 可选择向上爬 1 阶或 2 阶
state = 0 # 从第 0 阶开始爬
res = [0] # 使用 res[0] 记录方案数量
backtrack(choices, state, n, res)
res.first
end

### Driver Code ###
if __FILE__ == $0
n = 9

res = climbing_stairs_backtrack(n)
puts "爬 #{n} 阶楼梯共有 #{res} 种方案"
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
=begin
File: climbing_stairs_constraint_dp.rb
Created Time: 2024-05-29
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
=end

### 带约束爬楼梯:动态规划 ###
def climbing_stairs_backtrack(n)
return 1 if n == 1 || n == 2

# 初始化 dp 表,用于存储子问题的解
dp = Array.new(n + 1) { Array.new(3, 0) }
# 初始状态:预设最小子问题的解
dp[1][1], dp[1][2] = 1, 0
dp[2][1], dp[2][2] = 0, 1
# 状态转移:从较小子问题逐步求解较大子问题
for i in 3...(n + 1)
dp[i][1] = dp[i - 1][2]
dp[i][2] = dp[i - 2][1] + dp[i - 2][2]
end

dp[n][1] + dp[n][2]
end

### Driver Code ###
if __FILE__ == $0
n = 9

res = climbing_stairs_backtrack(n)
puts "爬 #{n} 阶楼梯共有 #{res} 种方案"
end
26 changes: 26 additions & 0 deletions codes/ruby/chapter_dynamic_programming/climbing_stairs_dfs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
=begin
File: climbing_stairs_dfs.rb
Created Time: 2024-05-29
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
=end

### 搜索 ###
def dfs(i)
# 已知 dp[1] 和 dp[2] ,返回之
return i if i == 1 || i == 2
# dp[i] = dp[i-1] + dp[i-2]
dfs(i - 1) + dfs(i - 2)
end

### 爬楼梯:搜索 ###
def climbing_stairs_dfs(n)
dfs(n)
end

### Driver Code ###
if __FILE__ == $0
n = 9

res = climbing_stairs_dfs(n)
puts "爬 #{n} 阶楼梯共有 #{res} 种方案"
end
33 changes: 33 additions & 0 deletions codes/ruby/chapter_dynamic_programming/climbing_stairs_dfs_mem.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
=begin
File: climbing_stairs_dfs_mem.rb
Created Time: 2024-05-29
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
=end

### 记忆化搜索 ###
def dfs(i, mem)
# 已知 dp[1] 和 dp[2] ,返回之
return i if i == 1 || i == 2
# 若存在记录 dp[i] ,则直接返回之
return mem[i] if mem[i] != -1

# dp[i] = dp[i-1] + dp[i-2]
count = dfs(i - 1, mem) + dfs(i - 2, mem)
# 记录 dp[i]
mem[i] = count
end

### 爬楼梯:记忆化搜索 ###
def climbing_stairs_dfs_mem(n)
# mem[i] 记录爬到第 i 阶的方案总数,-1 代表无记录
mem = Array.new(n + 1, -1)
dfs(n, mem)
end

### Driver Code ###
if __FILE__ == $0
n = 9

res = climbing_stairs_dfs_mem(n)
puts "爬 #{n} 阶楼梯共有 #{res} 种方案"
end
40 changes: 40 additions & 0 deletions codes/ruby/chapter_dynamic_programming/climbing_stairs_dp.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
=begin
File: climbing_stairs_dp.rb
Created Time: 2024-05-29
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
=end

### 爬楼梯:动态规划 ###
def climbing_stairs_dp(n)
return n if n == 1 || n == 2

# 初始化 dp 表,用于存储子问题的解
dp = Array.new(n + 1, 0)
# 初始状态:预设最小子问题的解
dp[1], dp[2] = 1, 2
# 状态转移:从较小子问题逐步求解较大子问题
(3...(n + 1)).each { |i| dp[i] = dp[i - 1] + dp[i - 2] }

dp[n]
end

### 爬楼梯:空间优化后的动态规划 ###
def climbing_stairs_dp_comp(n)
return n if n == 1 || n == 2

a, b = 1, 2
(3...(n + 1)).each { a, b = b, a + b }

b
end

### Driver Code ###
if __FILE__ == $0
n = 9

res = climbing_stairs_dp(n)
puts "爬 #{n} 阶楼梯共有 #{res} 种方案"

res = climbing_stairs_dp_comp(n)
puts "爬 #{n} 阶楼梯共有 #{res} 种方案"
end
65 changes: 65 additions & 0 deletions codes/ruby/chapter_dynamic_programming/coin_change.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
=begin
File: coin_change.rb
Created Time: 2024-05-29
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
=end

### 零钱兑换:动态规划 ###
def coin_change_dp(coins, amt)
n = coins.length
_MAX = amt + 1
# 初始化 dp 表
dp = Array.new(n + 1) { Array.new(amt + 1, 0) }
# 状态转移:首行首列
(1...(amt + 1)).each { |a| dp[0][a] = _MAX }
# 状态转移:其余行和列
for i in 1...(n + 1)
for a in 1...(amt + 1)
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]] + 1].min
end
end
end
dp[n][amt] != _MAX ? dp[n][amt] : -1
end

### 零钱兑换:空间优化后的动态规划 ###
def coin_change_dp_comp(coins, amt)
n = coins.length
_MAX = amt + 1
# 初始化 dp 表
dp = Array.new(amt + 1, _MAX)
dp[0] = 0
# 状态转移
for i in 1...(n + 1)
# 正序遍历
for a in 1...(amt + 1)
if coins[i - 1] > a
# 若超过目标金额,则不选硬币 i
dp[a] = dp[a]
else
# 不选和选硬币 i 这两种方案的较小值
dp[a] = [dp[a], dp[a - coins[i - 1]] + 1].min
end
end
end
dp[amt] != _MAX ? dp[amt] : -1
end

### Driver Code ###
if __FILE__ == $0
coins = [1, 2, 5]
amt = 4

# 动态规划
res = coin_change_dp(coins, amt)
puts "凑到目标金额所需的最少硬币数量为 #{res}"

# 空间优化后的动态规划
res = coin_change_dp_comp(coins, amt)
puts "凑到目标金额所需的最少硬币数量为 #{res}"
end
63 changes: 63 additions & 0 deletions codes/ruby/chapter_dynamic_programming/coin_change_ii.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
=begin
File: coin_change_ii.rb
Created Time: 2024-05-29
Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com)
=end

### 零钱兑换 II:动态规划 ###
def coin_change_ii_dp(coins, amt)
n = coins.length
# 初始化 dp 表
dp = Array.new(n + 1) { Array.new(amt + 1, 0) }
# 初始化首列
(0...(n + 1)).each { |i| dp[i][0] = 1 }
# 状态转移
for i in 1...(n + 1)
for a in 1...(amt + 1)
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]]
end
end
end
dp[n][amt]
end

### 零钱兑换 II:空间优化后的动态规划 ###
def coin_change_ii_dp_comp(coins, amt)
n = coins.length
# 初始化 dp 表
dp = Array.new(amt + 1, 0)
dp[0] = 1
# 状态转移
for i in 1...(n + 1)
# 正序遍历
for a in 1...(amt + 1)
if coins[i - 1] > a
# 若超过目标金额,则不选硬币 i
dp[a] = dp[a]
else
# 不选和选硬币 i 这两种方案之和
dp[a] = dp[a] + dp[a - coins[i - 1]]
end
end
end
dp[amt]
end

### Driver Code ###
if __FILE__ == $0
coins = [1, 2, 5]
amt = 5

# 动态规划
res = coin_change_ii_dp(coins, amt)
puts "凑出目标金额的硬币组合数量为 #{res}"

# 空间优化后的动态规划
res = coin_change_ii_dp_comp(coins, amt)
puts "凑出目标金额的硬币组合数量为 #{res}"
end
Loading

0 comments on commit a14be17

Please sign in to comment.