2025 프로그래머스 코드챌린지 1차 예선 Lv2. 지게차와 크레인

2025 프로그래머스 코드챌린지 1차 예선 > Lv2. 지게차와 크레인

프로그래머스 문제

문제 설명

A 회사의 물류창고에는 알파벳 대문자로 종류를 구분하는 컨테이너가 세로로 n 줄, 가로로 m줄 총 n x m개 놓여 있습니다. 특정 종류 컨테이너의 출고 요청이 들어올 때마다 지게차로 창고에서 접근이 가능한 해당 종류의 컨테이너를 모두 꺼냅니다. 접근이 가능한 컨테이너란 4면 중 적어도 1면이 창고 외부와 연결된 컨테이너를 말합니다.

최근 이 물류 창고에서 창고 외부와 연결되지 않은 컨테이너도 꺼낼 수 있도록 크레인을 도입했습니다. 크레인을 사용하면 요청된 종류의 모든 컨테이너를 꺼냅니다.

시각화

하나의 케이스를 시각화하면 아래와 같다.

지게차와크레인-1.png

A 를 지게차로 꺼낸다면 아래와 같이 뺄 수 있다. 지게차로 가져올 때는 외부에서 접근할 수 있는 컨테이너만 가져올 수 있다.

지게차와크레인-2.png

B 를 크레인으로 꺼낸 경우는 아래와 같다. 크레인은 창고 내부에 있는 컨테이너를 모두 꺼낼 수 있다.

즉 크레인을 사용하면 모든 컨테이너를 꺼낼 수 있다.

지게차와크레인-3.png

다시 A 를 지게차로 꺼내는 경우는 아래와 같다. A 두 개가 붙어있지만 외부에서 접근할 수 있는 것은 하나뿐이다.

지게차와크레인-4.png

이러한 방식으로 컨테이너를 꺼내고 남은 컨테이너의 개수를 구하는 문제이다.

지게차와크레인-5.png

문제 해결 과정

간단하게 코드를 작성하고 싶었지만 그리고 그럴려고 했지만 코드가 길어져 아쉰 마음이 가득하다.

그렇지만 코드 자체보다는 접근법을 설명하고자 한다.

우선 첫 번째로 고민했던 것은 지게차로 가져갈 수 있는 컨테이너를 가지고 있을지 또는 지게차로 가져갈 수 있는 컨테이너 외부 영역 좌표 값을 저장해야 할지에 대한 것이었다. 이 고민에 대해서는 지게차로 가져갈 수 있는 컨테이너를 저장하는 것이 더 효율적이라고 판단했다. 이유는 컨테이너 외부 영역 좌표를 가지고 있다면 모든 컨테이너를 돌며 매번 상하좌우를 확인해야하기 때문이다. 이로 인해 더 비효율적일 것이라고 판단했다.

이제 시각화를 통해 어떻게 풀었는지 보자.

컨테이너 외부 영역 추가

연한 초록색으로 컨테이너를 감싸도록 하였다. 연한 초록색의 역할은 컨테이너 상하좌우에 연한 초록색이 있다면 지게차로 가져갈 수 있는 컨테이너라는 것을 의미한다.

지게차와크레인-6.png

왜 연한 초록색이 필요할까? 아래와 같이 D 가 크레인으로 뽑혀갈 때 컨테이너 안에 감싸져 외부 영역에서 접근할 수 없는 영역이 되었다.

지게차와크레인-7.png

크레인으로 제거할 때 연한 초록색과 닿아있는 부분이 있다면 지게차로 가져갈 수 있는 영역이 생긴 것이라는 것을 의미한다.

지게차와크레인-8.png

작업 요청에 따른 컨테이너 제거

A

진한 초록색은 지게차로 가져갈 수 있는 컨테이너를 의미한다. A 를 지게차로 가져갈 때는 아래와 같이 지게차로 가져갈 수 있는 컨테이너를 모두 제거한다. 그 후 연결 가능한 연한 초록색이 있다면 지게차로 가져갈 수 있는 컨테이너를 추가한다.

지게차와크레인-9.png

BB

BB 를 크레인으로 제거할 때는 아래와 같이 모두 제거한다. 왼쪽에 BB 는 2개가 붙어있고 외부와 연결이 가능하기에 연한 초록색 영역으로 처리해야한다.

지게차와크레인-10.png

A

A 를 다시 지게차로 가져가면 아래 그림과 같이 하나만 가져갈 수 있다. 오른쪽에 있는 A 는 갇혀있기 때문이다.

지게차와크레인-11.png

전체 코드

위 내용을 코드로 구현하면 아래와 같다.

참고하고 싶다면 코드 자체보다는 위에 시각화한 그림을 참고하길 바란다.

from collections import defaultdict, deque


def solution(storage, requests):
    def is_valid_yx(y, x):
        return 0 <= y < len(storage) or 0 <= x < len(storage[0])

    answer = len(storage) * len(storage[0])

    for y in range(len(storage)):
        storage[y] = [''] + [a for a in storage[y]] + ['']

    storage.insert(0, ['' for _ in storage[0]])
    storage.append(['' for _ in storage[0]])

    available_storages = defaultdict(set)
    for y in range(len(storage)):
        for x in range(len(storage[0])):
            if not ((y == (0 + 1) or y == len(storage) - 1 - 1) or (x == 0 + 1 or x == len(storage[0]) - 1 - 1)):
                continue
            available_storages[storage[y][x]].add((y, x))

    for req in requests:
        def explore_available_storage(yx):
            y, x = yx[0], yx[1]
            is_able = False
            explore_yx = {(y, x)}
            pop_yx = {(y, x)}
            possible_yx, visit_yx = set(), set()

            while explore_yx:
                ey1, ex1 = explore_yx.pop()

                for py1, px1 in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
                    y2, x2 = ey1 + py1, ex1 + px1
                    if not is_valid_yx(y2, x2) or (y2, x2) in visit_yx:
                        continue

                    if storage[y2][x2] == '':
                        is_able = True
                    if storage[y2][x2] == 'POP':
                        explore_yx.add((y2, x2))
                        pop_yx.add((y2, x2))

                        if len(req) == 1:
                            storage[y2][x2] = ''
                    else:
                        possible_yx.add((y2, x2))

                        if len(req) == 1:
                            available_storages[storage[y2][x2]].add((y2, x2))

                    visit_yx.add((y2, x2))
            if is_able:
                for yx in pop_yx:
                    storage[yx[0]][yx[1]] = ''
                for yx in possible_yx:
                    available_storages[storage[yx[0]][yx[1]]].add((yx[0], yx[1]))

        if len(req) == 1:
            if req not in available_storages:
                continue

            containers_yx = available_storages[req]
            for yx in containers_yx.copy():
                y1, x1 = yx
                if storage[y1][x1] == '':
                    continue
                answer -= 1
                storage[y1][x1] = ''

                explore_available_storage((y1, x1))
        else:
            for y in range(len(storage)):
                for x in range(len(storage[0])):
                    if storage[y][x] != req[0]:
                        continue

                    answer -= 1
                    storage[y][x] = 'POP'
                    explore_available_storage((y, x))

    return answer

지게차와크레인-12.png


2025 프로그래머스 코드챌린지 1차 예선 Lv1. 유연근무제

2025 프로그래머스 코드챌린지 1차 예선 > Lv1. 유연근무제

NCloud LB & SourcePipeline 구축하기
tech collection 서비스 성능 개선하기
Selenium 복권 구매 자동화 만들어보기
디자인 패턴
책 리뷰
블로그 챌린지