알고리즘/코딩테스트 준비

다익스트라 알고리즘 활용 문제 (프로그래머스 level 4 - 미로찾기)

에멜라 2023. 3. 7. 17:38

level 4 - 미로찾기

https://school.programmers.co.kr/learn/courses/30/lessons/81304

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

문제 자체가 다익스트라로 최적화 할 뿐 아니라 trap을 밟는경우 안밟는 경우를 나눠서 두개의 다익스트라를 만들어야 할 것 같다.

 

trap을 밟음으로 인해서 화살표가 반대방향으로 바뀌는 상황을 swap_graph 와 graph를 계속 교차하면서 참고하도록 구현했는데, 기존의 다익스트라 방법을 통해서는 swap을 통해서만 갈 수 있는 노드를 계산하지 못한다(왜? swap을 밟아야 도달가능한 경우, 이와 연결된 연결노드에 도달했을 때 다익스트라 최솟값을 갖는 경우가 아니기 때문에 탐색을 진행하지 않게 된다)

 

즉, 각 노드는, trap을 밟았을 때의 최솟값(활성화 됐을 때), trap을 밟지 않았을 때의 최솟값(트랩 비활성화) 의 최솟값을 모두 들고있어야 구현 가능하다.

 

 

+++

기존의 문제 해석을 단순히 trap의 on off 로서 생각을 했고, trap on 시에 전체 노드의 방향이 바뀌는줄 알았는데, 그게 아니라 연결노드만 바뀐다. 즉 문제 상황이 더 복잡해 질 수 있는데, 2개의 노드가 둘다 trap 상황이라면, 두개의 노드 각각이 켜져있는지 꺼져있는지 즉 2의 2승인 4가지의 경우 모두 다른 상황이 된다.

 

(질문하기)의 대부분 풀이 역시 각 트랩에 대한 visit를 만들고, 그 상황마다의 다익스트라 알고리즘으로 구한 최솟값을 모두 계산하는것으로 보여진다.(트랩이 최대 10개이므로 최대 경우의 수는 2의 10승인 1024)

 

 

 

 

기존 코드 (수정 필요)

import heapq
from collections import defaultdict
Inf = 100000


def solution(n, start, end, roads, traps):
    dij = [Inf for i in range(n+1)]          # 다익스트라 알고리즘
    dij_swap = [Inf for i in range(n+1)]
    dij[start] = 0                           # 시작값으로부터의 거리이므로 시작값에서의 초기 거리는 0
    queue = []                                   # 다익스트라 알고리즘으로 최소거리를 찾아나갈 때, 
    heapq.heappush(queue,[dij[start],start])     # 간선거리를 무작위로 받으면 최솟값 탐색이 느려짐. 개선하기 위해 힙을 사용 
    
    graph = defaultdict(list)              # graph[시작점] = [[도착점, 거리], ... ] 
    swap_graph = defaultdict(list)
    
    for p, q, s in roads :                 # graph dict, swap_graph dict 생성
        graph[p].append([q,s])
        swap_graph[q].append([p,s])
    
    trap_switch = False
    def traps_dij (traps, trap_switch, queue) :
        
        # 도착점이 traps 원소라면, 다익스트라 거리 계산 후에 trap_switch가 반대인 경우로 넘어가야함.
        
        if trap_switch :
            use_g = swap_graph
        else :
            use_g = graph
        
        while queue :
            cumsum_sec,p_room = heapq.heappop(queue)
            
            # 만약 이 길을 통해 온 노드까지의 시간이, 기존의 최소 시간보다 길면 더 볼필요가 없다
            if dij[p_room] < cumsum_sec :         
                continue
            
            for q, s in use_g[p_room] :
                new_sec = cumsum_sec + s
                if new_sec < dij[q] :
                    dij[q] = new_sec
                    if q not in traps :
                        heapq.heappush(queue,[dij[q],q])
                    else :
                        new_queue = []
                        heapq.heappush(new_queue,[dij[q],q])
                        new_trap_switch = not trap_switch
                        traps_dij (traps, new_trap_switch, new_queue) 
    
    traps_dij(traps, trap_switch, queue)
    
    return min(dij[end],dij_swap[end])