Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Ruby code - chapter "Dynamic Programming" #1378

Merged
merged 1 commit into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 ([email protected])
=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 ([email protected])
=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 ([email protected])
=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 ([email protected])
=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 ([email protected])
=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 ([email protected])
=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 ([email protected])
=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