[python] 카드게임(Programmers)

프로그래머스의 "카드 게임"문제(Level4)를 동적계획법을 사용하여 해결한다.

Posted by Seoyoung Lee on May 31, 2020 · 4 mins read

문제(Link) : https://programmers.co.kr/learn/courses/30/lessons/42896


프로그래머스에 있는 동적계획법(Dynamic Programming) 문제이다. 먼저 점화식을 세웠다. 왼쪽 카드와 오른쪽 카드가 있으므로 2차원 배열 dp를 선언했고 내가 세운 점화식은 아래와 같다.

# dp[i][j] : left에서 i번째 카드, right에서 j번째 카드
dp[i][j] = max(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]+right[j-1])

dp[i-1][j-1]은 left, right 두 개의 카드를 모두 버리는 경우이고, dp[i-1][j]는 left 카드를 버리는 경우이다. 마지막으로 dp[i][j-1]+right[j-1]는 left카드가 right카드보다 큰 경우에만 해당하며 right카드를 버림으로써 점수를 얻는 경우이다.

def solution(left, right):
    if max(left) > max(right):
        return sum(right)
    dp = [[0 for _ in range(len(right)+1)] for _ in range(len(left)+1)]

    for i in range(1, len(left)+1):
        for j in range(1, len(right)+1):
            dp[i][j] = max(dp[i-1][j-1], dp[i-1][j])
            if left[i-1] > right[j-1]:
                dp[i][j] = dp[i][j-1]+right[j-1]

    return max(dp[-1])

이렇게 코드를 구현했을 때, 한가지 테스트케이스에서 넘어가지 않았다.
solution([2, 1, 1], [3, 1, 1])인 경우였는데, 기존 코드로는 해결할 수 없는 문제였다.

검색해본 결과, 반복문을 이용해서 푸는 방법은 0번째부터 dp배열을 채우는 것이 아닌 reversed()를 사용해 (n-1)번째부터 역방향으로 dp배열을 채우면 된다고 한다. 잘 이해는 안되지만 코드는 아래와 같다.

def solution_ok(left, right):
    if max(left) > max(right):
        return sum(right)
    dp = [[0 for _ in range(len(right)+1)] for _ in range(len(left)+1)]

    for i in reversed(range(len(left))):
        for j in reversed(range(len(right))):
            if left[j] > right[i]:
                dp[i][j] = dp[i+1][j] + right[i]
            else:
                dp[i][j] = max(dp[i+1][j+1], dp[i][j+1])

    return dp[0][0]