목차

카드 짝 맞추기

ps
링크programmers.co.kr/…
출처프로그래머스
문제 번호72415
문제명카드 짝 맞추기
레벨Level 3
분류

구현, BFS, DP

시간복잡도O(n^2 * 2^n)
인풋사이즈n<=6
사용한 언어Python
해결날짜2021/01/29
출처

ps:problems:programmers:2021_kakao_blind_recruitment

풀이

코드

"""Solution code for "Programmers 72415. 카드 짝 맞추기".

- Problem link: https://programmers.co.kr/learn/courses/30/lessons/72415
- Solution link: http://www.teferi.net/ps/problems/programmers/72415
"""

import collections
import functools

INF = float('inf')


def solution(board, r, c):

    def next_pos(pos, cards):
        r, c = pos
        for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)):
            nr, nc = r + dr, c + dc
            if 0 <= nr < 4 and 0 <= nc < 4:
                yield nr, nc
            for i in range(1, 5):
                nr, nc = r + dr * i, c + dc * i
                if not (0 <= nr < 4 and 0 <= nc < 4):
                    yield nr - dr, nc - dc
                    break
                elif board[nr][nc] in cards:
                    yield nr, nc
                    break

    @functools.lru_cache(maxsize=None)
    def bfs(start, target, cards):
        if start == target:
            return 0
        visited = set()
        queue = collections.deque([(start, 0)])
        while queue:
            cur, dist = queue.popleft()
            for next_ in next_pos(cur, cards):
                if next_ == target:
                    return dist + 1
                if next_ not in visited:
                    visited.add(next_)
                    queue.append((next_, dist + 1))
        return INF

    @functools.lru_cache(maxsize=None)
    def min_dist_rec(cur_pos, cards):
        if not cards:
            return 0
        min_dist = INF
        for card_to_clear in cards:
            pos1, pos2 = pos[card_to_clear]
            next_cards = tuple(card for card in cards if card != card_to_clear)
            dist1 = (bfs(cur_pos, pos2, cards) + bfs(pos2, pos1, cards) +
                     min_dist_rec(pos1, next_cards))
            dist2 = (bfs(cur_pos, pos1, cards) + bfs(pos1, pos2, cards) +
                     min_dist_rec(pos2, next_cards))
            min_dist = min(min_dist, dist1, dist2)
        return min_dist

    pos = collections.defaultdict(list)
    for r_, row in enumerate(board):
        for c_, card in enumerate(row):
            if card != 0:
                pos[card].append((r_, c_))
    cards = tuple(pos)
    return len(cards) * 2 + min_dist_rec((r, c), cards)