9클럽 코테 스터디 40일차 입니다. 오늘은 8월 30일, 8월이 끝나가네요,,, 시간이 정말 빠르다는것을 느낍니다..
오늘 문제는 Dynamic Programming 유형의 문제입니다.
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 <= 1000
0 <= cost[i] <= 999
이 문제를 해결하기 위해 동적 프로그래밍(dynamic programming) 방식을 사용할 수 있습니다. 목표는 계단의 각 단계까지 도달하는 데 드는 최소 비용을 계산한 후, 최종적으로 계단의 맨 위까지 도달하는 데 필요한 최소 비용을 찾는 것입니다.
dp[i]
를 계단의 i
번째 단계에 도달하는 데 드는 최소 비용이라고 정의합니다.dp[0]
은 첫 번째 단계에 도달하는 비용으로, 이는 cost[0]
입니다.dp[1]
은 두 번째 단계에 도달하는 비용으로, 이는 cost[1]
입니다.i
번째 단계에 도달하기 위해서는 i-1
번째 단계나 i-2
번째 단계에서 올 수 있습니다. 따라서 i
번째 단계에 도달하는 비용은 현재 단계의 비용과 이전 두 단계 중 더 작은 비용을 더한 값이 됩니다:
dp[n-1]
)나 그 전 단계(dp[n-2]
)에서 도달할 수 있으므로, 이 둘 중 작은 값을 반환합니다:
여기서 n
은 계단의 총 단계 수입니다.
아래는 이 문제를 해결하는 Python 코드입니다:
class Solution(object):
def minCostClimbingStairs(self, cost):
"""
:type cost: List[int]
:rtype: int
"""
n = len(cost)
dp = [0] * n
dp[0] = cost[0]
dp[1] = cost[1]
for i in range(2,n):
dp[i] = cost[i] + min(dp[i-1],dp[i-2])
return min(dp[n-1],dp[n-2])
cost = [10, 15, 20]
print(minCostClimbingStairs(cost)) # 출력: 15
cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
print(minCostClimbingStairs(cost)) # 출력: 6
이 접근법은 아래로부터 위로 쌓아 올리는 동적 프로그래밍 방식으로, 시간 복잡도는 , 공간 복잡도는 입니다.
https://leetcode.com/problems/min-cost-climbing-stairs/submissions/1372970318
처음 구현은 이렇게 진행했는데, 주어진 변수 cost
가 있기 때문에 이것을 이용한다면 추가적인 메모리 공간을 사용하지 않고, 이것을 수정하여 최소 비용을 계산할 수 있습니다.
dp
를 생성하여 메모리를 사용했습니다. 하지만 이 코드에서는 입력 리스트 cost
를 직접 수정하기 때문에 추가적인 메모리 공간을 사용하지 않고도 결과를 계산할 수 있습니다. 따라서 공간 복잡도가 로 줄어듭니다.이 코드는 cost
리스트를 직접 수정하여 최소 비용을 계산합니다. 이를 위해, 각 단계에서 이전 두 단계(i-1
과 i-2
)의 최소 비용을 현재 단계 비용에 더합니다. 최종적으로, 리스트의 마지막 단계(cost[n-1]
)와 그 전 단계(cost[n-2]
) 중 더 작은 값을 반환합니다.
class Solution(object):
def minCostClimbingStairs(self, cost):
"""
:type cost: List[int]
:rtype: int
"""
n = len(cost)
for i in range(2,n):
cost[i] += min(cost[i-1],cost[i-2])
return min(cost[n-1],cost[n-2])
이 코드가 원래 코드보다 효율적입니다. 특히, dp
배열을 따로 생성하지 않음으로써 공간 복잡도를 줄였기 때문에, 메모리 효율성이 더 좋습니다. 시간 복잡도는 두 코드 모두 으로 동일합니다. 따라서, 주어진 코드가 더 효율적이라고 할 수 있습니다.
https://leetcode.com/problems/min-cost-climbing-stairs/submissions/1372970716
두 코드의 시간복잡도는 로 동일하지만 혹시나 해서 개인적으로 시간 측정을 진행해보았습니다.
class Solution(object):
def minCostClimbingStairs(self, cost):
"""
:type cost: List[int]
:rtype: int
"""
n = len(cost)
dp = [0] * n
dp[0] = cost[0]
dp[1] = cost[1]
for i in range(2,n):
dp[i] = cost[i] + min(dp[i-1],dp[i-2])
return min(dp[n-1],dp[n-2])
def minCostClimbingStairs1(self, cost):
"""
:type cost: List[int]
:rtype: int
"""
n = len(cost)
for i in range(2,n):
cost[i] += min(cost[i-1],cost[i-2])
return min(cost[n-1],cost[n-2])
if __name__ == '__main__':
import time
t = time.time()
a = Solution()
res1 = a.minCostClimbingStairs([10,15,20])
res2 = a.minCostClimbingStairs([1,100,1,1,1,100,1,1,100,1])
print(f"Test Case1 Result: {res1}")
print(f"Test Case2 Result: {res2}")
print(f"{time.time()-t:.6f}초")
t = time.time()
a = Solution()
res1 = a.minCostClimbingStairs1([10,15,20])
res2 = a.minCostClimbingStairs1([[1,100,1,1,1,100,1,1,100,1]])
print(f"Test Case1 Result: {res1}")
print(f"Test Case2 Result: {res2}")
print(f"{time.time()-t:.6f}초")
Test Case1 Result: 15
Test Case2 Result: 6
0.000028초
Test Case1 Result: 15
Test Case2 Result: 6
0.000009초
공간적으로 더 효율적이다보니 시간 복잡도는 동일하지만, 실제 속도는 두 번째 코드가 더 빠른 것을 확인할 수 있습니다.
99클럽 코테 스터디에서 dp 알고리즘 유형은 오랜만이였습니다. 확실히 오랜만이다보니 처음에 좀 버벅였는데요. dp 알고리즘도 기업 코테에서 빈출 유형 알고리즘으로 알고있어서, 이 유형도 반복이 필요할 것으로 보입니다.
읽어주셔서 감사합니다!
더 나은 개발자가 되기를.. ! 💪