Bài toán
Link đến bài toán: 746. Min Cost Climbing Stairs
Phân tích
Ta có thể dễ dàng lập công thức đệ quy để giải bài toán này:
f(n) = min(f(n - 1), f(n - 2)) + cost[n]
Tuy nhiên, sao phải dùng đệ quy khi ta có thể dùng dynamic programming? (=^OwO^=)
Ta có thể dùng một mảng dp để lưu lại kết quả của các bài toán con đã được giải.
Với dp[i] là chi phí nhỏ nhất để đi đến bậc thang thứ i.
dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]
Độ phức tạp của giải thuật là O(n), và độ phức tạp không gian là O(n).
Nếu như quan sát kĩ hơn, ta thấy rằng ta chỉ cần lưu lại hai giá trị dp[i - 1] và dp[i - 2] thôi.
Lúc này, độ phức tạp không gian sẽ là O(1).
Giải thuật
Đệ quy
1. Nếu `n == 0` hoặc `n == 1` thì `f(n) = cost[n]`.
2. Nếu `n > 1` thì `f(n) = min(f(n - 1), f(n - 2)) + cost[n]`.
Dynamic programming
1. Khởi tạo mảng `dp` với `dp[0] = cost[0]` và `dp[1] = cost[1]`.
2. Với `i` từ `2` đến `len(cost) - 1`:
1. `dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]`.
3. Trả về `min(dp[len(cost) - 1], dp[len(cost) - 2])`.
Dynamic programming (không dùng mảng)
1. Khởi tạo `dp0 = cost[0]` và `dp1 = cost[1]`.
2. Với `i` từ `2` đến `len(cost) - 1`:
1. `dp2 = min(dp1, dp0) + cost[i]`.
2. `dp0 = dp1`.
3. `dp1 = dp2`.
3. Trả về `min(dp1, dp0)`.
Code
Đệ quy
func minCostClimbingStairs(cost []int) int {
n := len(cost)
if n == 0 || n == 1 {
return cost[n - 1]
}
return min(minCostClimbingStairs(cost[:n - 1]), minCostClimbingStairs(cost[:n - 2])) + cost[n - 1]
}
Dynamic programming
func minCostClimbingStairs(cost []int) int {
n := len(cost)
dp := make([]int, n)
dp[0] = cost[0]
dp[1] = cost[1]
for i := 2; i < n; i++ {
dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]
}
return min(dp[n - 1], dp[n - 2])
}
Dynamic programming (không dùng mảng)
func minCostClimbingStairs(cost []int) int {
n := len(cost)
dp0 := cost[0]
dp1 := cost[1]
for i := 2; i < n; i++ {
dp2 := min(dp1, dp0) + cost[i]
dp0 = dp1
dp1 = dp2
}
return min(dp1, dp0)
}