import heapq
def solution(n, start, end, roads, traps):
answer = 0
INF = float('inf')
dp = [[INF for _ in range(n+1)] for _ in range(1<<len(traps))]
traps_index ={value:ind for ind,value in enumerate(traps) }
node_list = []
graph =[[] for _ in range(n+1)]
for road in roads:
x,y,pay = road
graph[x].append([y,pay,0])
graph[y].append([x,pay,1])
heapq.heappush(node_list,(0,start,0))
dp[0][start] = 0
while node_list:
cur_time,cur_node,state = heapq.heappop(node_list)
if end == cur_node:
answer = cur_time
break
if dp[state][cur_node] < cur_time:
continue
for next_node,pay,flag in graph[cur_node]:
next_state = state
if cur_node in traps:
if next_node in traps:
cur_flag = ((1&(state>>traps_index[cur_node])) +
(1&(state>>traps_index[next_node])))%2
next_state = state^(1<<traps_index[next_node])
else:
cur_flag = (1&(state>>traps_index[cur_node]))
else:
if next_node in traps:
cur_flag = (1&(state>>traps_index[next_node]))
next_state = state^(1<<traps_index[next_node])
else:
cur_flag = 0
if cur_flag == flag:
if dp[next_state][next_node] > cur_time + pay:
dp[next_state][next_node] = cur_time + pay
if next_node in traps:
heapq.heappush(node_list,(dp[next_state][next_node], next_node,next_state ))
else:
heapq.heappush(node_list,(dp[next_state][next_node],next_node,next_state))
return answer
이 문제를 핵심은 트랩을 밟았을 때의 상태를 어떻게 저장할 것인가.
저장한 그 상태를 통해 현재 길의 상태를 어떻게 알 수 있을가인가.
이 문제는 총 4가지 경우가 있다고 볼 수 있다.
트랩이 아닌 곳 -> 트랩이 아닌곳
트랩이 아닌 곳 -> 트랩인 곳
트랩인 곳 - > 트랩이 아닌곳
트랩인 곳 -> 트랩인 곳
이렇게 총 4가지 경우가 있을거고, 각각의 상태를 구분해서, 하면 된다.
우리는 먼저 주어진 경로들을 2가지 형태로 저장해놔야한다.
원래 주어진 정방향과, 이게 뒤집혔을때 가는 역방향이다. 이걸 나는 (다음노드, 시간, 상태) 로 넣어놨다.
0이면 정방향
1이면 역방향을 의미하는 식으로 넣어놨다.
트랩은 최대10개 밖에 없고, 트랩의 상태는 비트마스킹을 통해 나타낼수있다.
그렇게 하기 위해 각각의 trap들의 index로 각자의 위치를 매핑시켜줬다.
그러면 인제부터 각위치의 비트가 1이면 해당 위치의 트랩이 활성화 된 상황이고, 비활성화일때는 0이다.
그리고 우리는 각 상태에서 다익스트라를 돌리는 것이기 때문에 최대 (2**10) * N개의 distance 테이블을 만들어놓고 탐색을 하면 된다.
원래의 다익스트라와 비슷하게 하지만, 우리는 state를 통해 현재 길을 이용할 수 있는지 없는지 판별을 해줘야한다.
한 그래프에 정방향, 역방향을 전부 넣어놨기 때문에, state를 통해서 해당 길을 이용할 수 있는지 찾아야한다.
먼저 가장 쉬운
현재 노드가 트랩이 아닐때이다.
만약 다음 노드가 트랩이 아니라면, 이 길은 항상 정방향이다. 그러므로 저장해놓길 0으로 저장해놓은 길들만 이용이 가능하다.
만약 다음 노드가 트랩이라면, state를 통해 해당 트랩이 활성화 되어있는지 판별을 하면 된다.
판별하는 방법은 현재 state를 해당 트랩의 index만큼 >> 오른쪽으로 비트 이동을 시킨다. 그리고 1과 & 연산을 하면 된다.
이게 1이면 활성화가 된 상태이고, 아니면 비활성화 상태이다.
다음은 현재노드가 트랩일때이다.
현재노드가 트랩이고, 다음노드가 트랩이 아닐때에는 위와 동일하므로 넘어가겠다.
가장 많이 고민했던, 현재노드와 다음노드가 전부 트랩일때이다.
이때는 총 4가지 경우가 생긴다.
1. 현재노드 비활성화 다음노드 비활성화
2 .현재노드 활성화 다음노드 비활성화
3. 현재노드 비활성화 다음노드 활성화
4. 현재노드 활성화 다음노드 활성화
총 4가지 경우가 생긴다.
1번의 경우는 둘다 활성화가 안되어있기 때문에 정방향인 길들만 가면된다.
2,3번은 동일하고, 한쪽만 활성화 되어있으면, 그 길은 한번 뒤집힌거기 때문에 역방향인 길들만 가면 된다.
4번은 현재노드 다음노드 전부 활성화 되어있을때이다.
이때는 이 길이 2번 뒤집힌거기 때문에, 정방향인 길들만 가면된다.
그래서 나는
( ( 1 & (state>>traps_index[cur_node]) ) + ( 1&( state>>traps_index[next_node]) ) )%2
2개의 상태를 더해서 2로 나눈 나머지로 해서 정방향, 역방향을 구분을 해줬다. 이 외에도 xor 연산을 해도 된다.
우리는 이렇게 해서 해당 길이 갈 수 있는 길인지 아닌지 판별을 했다.
나머지는 다익스트라와 동일하게 하면되는데,
그리고 현재의 상태와 비교하는게 아니라, 다음의 상태와 비교를 해서 판별을 해주었다.
만약 다음 노드가 트랩이라 한다면, 그 트랩은 활성화가 된 상태인거고, 그때 최소값으로 들어가야한다고 생각했다
그래서 각 현재 시간과 다음노드까지 걸리는 시간은 현재 state가 아닌, 만약 다음노드가 트랩이라면, 트랩의 상태에 따라, 변화된 state로 비교를 해서 구현을 했다.
'알고리즘 > 프로그래머스' 카테고리의 다른 글
[프로그래머스] 92345 사라지는 발판 2022 KAKAO BLIND RECRUITMENT (0) | 2022.01.15 |
---|---|
[프로그래머스] 81305 시험장 나누기 카카오 채용연계형 인턴십 (0) | 2021.07.11 |
[프로그래머스] 81303 표 편집 2021 카카오 채용연계형 인턴십 (0) | 2021.07.11 |
[프로그래머스] 42641 체육복 (0) | 2021.03.14 |
[프로그래머스] 42840 모의고사 (0) | 2021.03.14 |