본문 바로가기

Algorithm/DP(Dynamic Programing)

(Python3) - LeetCode (Medium) : 1140. Stone Game II

반응형

https://leetcode.com/problems/stone-game-ii

 

Stone Game II - LeetCode

Can you solve this real interview question? Stone Game II - Alice and Bob continue their games with piles of stones. There are a number of piles arranged in a row, and each pile has a positive integer number of stones piles[i]. The objective of the game is

leetcode.com

memoization dp로 해결한 문제였습니다

📕 풀이방법

📔 입력 및 초기화

📑 memoization을 위한 dict mem, 현 index까지 누적합 sum, 더미의 길이 pile_len을 선언 후 적절히 초기화합니다

📔 풀이과정

alice가 x개를 가져간다면 bob은 다음에 1 <= x <= 2 * max(이전 x, 이전 M)만큼 가져갈 수 있습니다. 즉, 많이 가져갈 수록 상대방이 가져갈 수 있는 더미 길이가 늘어나게 됩니다. alice는 최적의 플레이를 위해 앞으로 x개를 가져갔을 때 bob은 최적의 플레이를 한다면 몇개를 가져갈 수 있을지 계산을 해야합니다. 이는 dp를 이용해 모든 경우의 수를 분석할 수 있도록 점화식을 세우는 것이 가장 간단해 보입니다. 

📑 점화식 설정
dp(piv, M): piv위치부터 현 플레이어가 1~2M개의 pile을 선택할 때 최종적으로 얻게되는 최대 돌 수

📑 dp함수 구현
1. 이미 계산된 mem이라면 이후 재귀함수는 호출할 필요가 없으므로 바로 반환합니다.

2. 현 플레이어가 최적 플레이시 piv 위치에서 얻을 수 있는 최댓값 best를 선언 후 0으로 초기화합니다.

3. 현 플레이어 차례에서 piv위치로부터 앞으로 남은 돌의 총합 remaining_candidate를 구해줍니다. piv가 0일때는 sum[-1]을 저장하도록 해주며 나머지의 경우는 sum[-1] - sum[piv-1] 입니다. 

4. 현 플레이어가 가져갈 수 있는 x의 범위만큼 순회합니다. 순회하며 각자 최선의 플레이를 했을 때 현 플레이어가 가져갈 수 있는 돌의 최댓값을 best에 저장합니다. piv위치에서 가져갈 수 있는 x의 범위는 1 <= x <= min(2*m, pile_len - piv) 입니다. 이 때 상대 플레이어의 최적 플레이시 가지게 될 돌 수이므로 dp(piv + x, max(m,x))가 됩니다. 현 플레이어는 remaining_candidate - 상대 플레이어의 최적 플레이시 가지게 될 돌 만큼을 가져가게 되며 이때의 최댓값은 best에 저장합니다

5. mem[(piv,m)]을 best값으로 갱신합니다

📑 시간 복잡도

O(N^3): x만큼 dp를 재귀적으로 호출하기 때문입니다

📑 공간 복잡도

O(N^2): memoization key가 piv, m 2차원이기 때문입니다.

📔 정답 출력 | 반환

dp(0,1)을 반환합니다.


📕 Code

📔 Python3

class Solution:
    def stoneGameII(self, piles: List[int]) -> int:
        mem = {}
        sum = [piles[0]]
        pile_len = len(piles)
        for i in range(1, pile_len):
            sum.append(sum[i-1] + piles[i])
        # dp(piv, M): piv위치부터 현 플레이어가 1~2M 개의 pile을 선택할 수 있을 때 최종적으로 얻는 최대 돌 개수 
        def dp(piv: int, m: int):
            if (piv,m) in mem:
                return mem[(piv,m)]
            best = 0
            remaining_candidate = 0
            if piv == 0:
                remaining_candidate = sum[-1]
            else:
                remaining_candidate = sum[-1] - sum[piv-1]
            for x in range(1, min(2*m, pile_len - piv)+1):
                current = dp(piv + x, max(m,x))
                opposite = remaining_candidate - current
                best = max(best, opposite)
            mem[(piv,m)] = best
            return mem[(piv,m)]

        return dp(0,1)

 


*더 나은 내용을 위한 지적, 조언은 언제나 환영합니다.