Thuật toán Coin Change Problem là một bài toán kinh điển trong lý thuyết thuật toán và lập trình, được sử dụng rộng rãi trong các vấn đề liên quan đến tối ưu hóa, lập trình động và lý thuyết đồ thị. Mục tiêu của bài toán là tìm ra cách tốt nhất để đổi một số tiền nhất định bằng cách sử dụng ít đồng xu nhất từ một tập hợp các mệnh giá đồng xu đã cho trước.
Để giải quyết vấn đề này, có nhiều cách tiếp cận khác nhau. Trong bài viết này, chúng ta sẽ xem xét hai phương pháp chính: sử dụng lập trình động và sử dụng đệ quy có ghi nhớ.
Phương pháp 1: Lập trình động
Lập trình động là một kỹ thuật tuyệt vời để giải quyết bài toán này vì nó giúp chúng ta tối ưu hóa các tính toán bằng cách lưu trữ kết quả của các phép tính trước đó để tái sử dụng trong tương lai.
Các bước thực hiện:
-
Khởi tạo một mảng
dp
: Ban đầu, tạo một mảngdp
với kích thước là số tiền cần đổi + 1. Giá trị củadp[i]
sẽ biểu thị số lượng ít nhất các đồng xu cần để tạo ra số tiềni
. -
Giá trị khởi đầu: Khởi tạo
dp[0]
= 0 vì không cần đồng xu nào để tạo ra số tiền 0. Tất cả các giá trị còn lại trongdp
sẽ được khởi tạo bằng một số lớn (vô cùng). -
Duyệt qua các mệnh giá: Duyệt qua mỗi mệnh giá trong tập hợp các đồng xu. Với mỗi mệnh giá, cập nhật mảng
dp
dựa trên các giá trị đã tính toán trước đó. -
Trả kết quả: Giá trị cuối cùng của
dp[số tiền cần đổi]
sẽ là kết quả cần tìm. Nếu giá trị này vẫn là số lớn (vô cùng), điều đó có nghĩa là không thể đổi được số tiền đó bằng các đồng xu có sẵn.
Mã nguồn minh họa bằng Python:
def coin_change(coins, amount):
# Khởi tạo mảng dp với số lượng lớn
dp = [float('inf')] * (amount + 1)
dp[0] = 0
# Duyệt qua các mệnh giá đồng xu
for coin in coins:
for x in range(coin, amount + 1):
dp[x] = min(dp[x], dp[x - coin] + 1)
return dp[amount] if dp[amount] != float('inf') else -1
# Ví dụ sử dụng
coins = [1, 2, 5]
amount = 11
print(coin_change(coins, amount)) # Output: 3 (11 = 5 + 5 + 1)
Phương pháp 2: Đệ quy có ghi nhớ
Phương pháp đệ quy có ghi nhớ cũng là một cách hiệu quả để giải quyết bài toán này, đặc biệt khi chúng ta muốn tận dụng lại các kết quả tính toán trước đó.
Các bước thực hiện:
-
Tạo một hàm đệ quy: Hàm này sẽ trả về số lượng ít nhất các đồng xu cần để đổi một số tiền nhất định.
-
Ghi nhớ các kết quả trung gian: Sử dụng một từ điển (dictionary) để lưu trữ các kết quả đã tính toán trước đó nhằm tránh các phép tính lặp lại.
-
Trả kết quả: Hàm đệ quy sẽ trả về số lượng ít nhất các đồng xu cần hoặc -1 nếu không thể đổi được số tiền đó.
Mã nguồn minh họa bằng Python:
def coin_change_recursive(coins, amount, memo={}):
if amount in memo:
return memo[amount]
if amount == 0:
return 0
if amount < 0:
return -1
min_coins = float('inf')
for coin in coins:
res = coin_change_recursive(coins, amount - coin, memo)
if res >= 0 and res < min_coins:
min_coins = res + 1
memo[amount] = min_coins if min_coins != float('inf') else -1
return memo[amount]
# Ví dụ sử dụng
coins = [1, 2, 5]
amount = 11
print(coin_change_recursive(coins, amount)) # Output: 3 (11 = 5 + 5 + 1)
Kết luận
Cả hai phương pháp lập trình động và đệ quy có ghi nhớ đều là các kỹ thuật hiệu quả để giải quyết bài toán Coin Change Problem. Tùy thuộc vào yêu cầu và giới hạn của bài toán mà người lập trình có thể chọn phương pháp phù hợp. Lập trình động thường được ưa chuộng hơn trong các tình huống yêu cầu tối ưu hóa về thời gian tính toán. Trong khi đó, đệ quy có ghi nhớ giúp việc cài đặt dễ dàng và trực quan hơn.
Comments