
어제 문제 Climbing Stairs와 유사한 문제입니다!! 오늘도 공부해 봅시다.
You are given an integer array cost where cost[i] is the cost of ith step on a staircase. Once you pay the cost, you can either climb one or two steps.
You can either start from the step with index 0, or the step with index 1.
Return the minimum cost to reach the top of the floor.
Example 1:
Input: cost = [10,15,20]
Output: 15
Explanation: You will start at index 1.
- Pay 15 and climb two steps to reach the top.
The total cost is 15.
Example 2:
Input: cost = [1,100,1,1,1,100,1,1,100,1]
Output: 6
Explanation: You will start at index 0.
- Pay 1 and climb two steps to reach index 2.
- Pay 1 and climb two steps to reach index 4.
- Pay 1 and climb two steps to reach index 6.
- Pay 1 and climb one step to reach index 7.
- Pay 1 and climb two steps to reach index 9.
- Pay 1 and climb one step to reach the top.
The total cost is 6.
Constraints:
2 <= cost.length <= 10000 <= cost[i] <= 999계단이 있습니다. 한 번 올라갈 때마다 1 step 또는 2 steps 올라갈 수 있습니다. 문제에서 정수형 배열 cost가 주어지는데, cost[i]는 i번째 계단을 밟았을 때 지불해야 하는 비용입니다. 처음 출발은 index 0 또는 index 1중 한 곳에서 시작할 수 있습니다.
이 계단의 꼭대기에 도착하기 위해 지불해야 하는 최소 비용을 반환해야 합니다.
이 문제의 핵심은 계단을 오를때 1 step 또는 2 steps을 오를수 있다는 점입니다. 계단을 오르면서 해당 위치의 비용을 확인해야 합니다.
그럼 단계를 밟아가보며 문제를 풀어봅시다!
cost[i]는 i번째 계단을 밟았을 때 지불해야 하는 비용입니다. 그리고 계단을 올라갈 때 1칸 또는 2칸 올라갈 수 있습니다. 그리고 원하는 output은 꼭대기까지 오르는데 드는 최소 비용입니다.
이 내용을 구현하는데 시간 복잡도는 까지 가능합니다. 딱 떠오르는 경우는 일단 완전 탐색입니다.
모든 경우를 생각해보는 것이죠! 문제 분석에서 진행해 보겠습니다.
첫 번째 예시를 보시죠!
Input: cost = [10,15,20]
Output: 15
Explanation: You will start at index 1.
- Pay 15 and climb two steps to reach the top.
The total cost is 15.
여기서 말하는 꼭대기는 cost 배열의 마지막 인덱스 + 1 지점을 의미합니다. 즉, 배열의 마지막 계단을 넘어서는 지점이 우리가 도착해야 할 목표입니다. 이를 시각화해 보면, 계단을 오르기 위해 최소 비용을 계산할 때 각 단계에서 선택할 수 있는 경로가 보입니다.

계단은 1 step 또는 2 steps을 올라갈 수 있습니다. 이 뜻은 꼭대기의 1칸, 2칸전까지의 비용을 보면 꼭대기까지 가는데 알 수 있다는 것이죠. 이것을 통해 떠오르는 것이 있습니다. 바로 재귀 함수를 활용하는 것입니다. 다른 예시를 한 번 더 보겠습니다.

이전 예시와 동일하게 꼭대기로부터 1칸, 두 칸 전의 결과를 보고 또 그 1칸, 2칸으로부터 1칸 2칸을 보는식입니다. 또 하나 알 수 있는 것은 재귀를 하면서 중복적으로 계산이 되는 것이 있는 것을 알 수 있습니다.
그렇다는 것은 !! 바로 동적 프로그래밍의 메모이제이션을 사용하면 된다는 것을 알아차릴 수 있습니다.
재귀를 활용한 완전 탐색으로 시작했지만 결국 이 문제는 동적 프로그래밍으로 더 효율적으로 구현할 수 있다는 것을 알게 되었습니다.
재귀 함수를 활용하면 의 시간복잡도가 나옵니다. 하지만 동적 프로그래밍을 사용하면 의 시간 복잡도가 나오게 됩니다.
이제 코드 구현을 하러 갈까요?
코드 구현을 해보겠습니다.
Top-Down
class Solution(object):
# Top-Down
def minCostClimbingStairs(self, cost):
"""
:type cost: List[int]
:rtype: int
"""
memo = {}
def dp(n):
if n == 0 or n == 1:
return 0
if n in memo:
return memo[n]
memo[n] = min(dp(n - 1) + cost[n - 1], dp(n - 2) + cost[n - 2])
return memo[n]
return dp(len(cost))
코드 설명:
dp(n-1)과 dp(n-2)를 계산하고, 각 계단까지 도달하는 최소 비용을 비교하여 결과를 메모이제이션 테이블에 저장합니다.len(cost)번째 계단에 도달하기 위한 최소 비용을 반환합니다.시간 복잡도:
n)을 한 번만 계산합니다.O(1))에 min() 연산을 수행하므로 전체 시간복잡도는 O(n)입니다.결과:
https://leetcode.com/problems/min-cost-climbing-stairs/submissions/1458009222

Top-Down으로 문제를 푼다면 Bottom-Up으로도 문제를 풀 수 있어야 합니다! 아래 Bottom-Up 코드입니다.
Bottom-Up
class Solution(object):
# # Bottom-Up
def minCostClimbingStairs(self, cost):
"""
:type cost: List[int]
:rtype: int
"""
n = len(cost)
dp = [0] * (n + 1)
for i in range(2, n + 1):
dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2])
return dp[n]
코드 설명:
dp[i]는 i번째 계단에 도달하기 위한 최소 비용을 나타냅니다.dp[i] = min(dp[i-1] + cost[i-1], dp[i-2] + cost[i-2])dp 배열을 순차적으로 채웁니다.n)에 도달하기 위한 최소 비용을 반환합니다.시간 복잡도:
n개의 계단을 순차적으로 계산하므로 시간복잡도는 O(n)입니다.결과:
https://leetcode.com/problems/min-cost-climbing-stairs/submissions/1458010504

이 문제는 동적 프로그래밍(DP)을 활용하여 효율적으로 해결할 수 있는 좋은 예제입니다. Top-Down 방식과 Bottom-Up 방식은 각기 다른 장단점을 가지고 있습니다.
이 문제를 통해 DP의 핵심 개념인 메모이제이션과 점화식 활용 방법을 다시 한 번 정리할 수 있었습니다.
쉽지 않은 DP, 반복 학습만이,, 살 길입니다!!
전체 코드는 다음 링크에서 확인할 수 있습니다.
읽어주셔서 감사합니다.
매일 발전하는 개발자가 될거야!
