[파이썬] 백준 5719 : 거의 최단 경로 (플레5)
5719번: 거의 최단 경로
입력은 여러 개의 테스트 케이스로 이루어져 있다. 각 테스트 케이스의 첫째 줄에는 장소의 수 N (2 ≤ N ≤ 500)과 도로의 수 M (1 ≤ M ≤ 104)가 주어진다. 장소는 0부터 N-1번까지 번호가 매겨져 있
www.acmicpc.net
문제
풀이
0. 방향성 생각
- 다익스트라를 돌려서 end node에 최소 비용으로 도달하는 경우를 찾고, 이 노드들이 어디서 오는지 체크한다.
- 체크를 위해서 trace 변수를 넣어서 각 node로 들어오는 node가 어떤 노드인지 기록한다.
- trace를 이용해서 node를 최단 경로 노드들을 모두 shortcut에 넣는다.
- 다익스트라를 한 번 더 돌려서 end node에 도달할 수 있는지 확인한다.
- 위 방식으로 접근하니까 75%에서 막혔음. (질문 게시판에 있는 83% 테케 참고)
- end node로 가는 cost는 5이고, 최단 경로는 3가지가 있음.
- 빨간 경로는 문제가 안되는데, 주황 경로랑 초록 경로가 2번 노드를 공통으로 지나친다.
- 기존의 코드대로면 end노드만 특별 취급해서 end에서 여러개의 trace를 탐색해서 start로 가는 경로를 모두 제거하는 문제였다.
- end node만 여러개의 리스트로 받을 게 아니라, 어떤 노드에 같은 cost로 들어오면 trace에 노드를 계속 추가해준다.
- 결론적으로 말하면, list에 int형으로 저장했던 코드를 list형으로 바꿨다.
- 기존 trace 추적에서는 while문으로 해도 됐다면, 바꾼 코드에서는 조금 더 복잡해져서 재귀로 DFS를 구현해서 shortcut에 최단 경로 edge들을 추가했다.
1. 다익스트라
def di(a,b):
visit = [inf]*n
visit[a] = 0
trace = [[] for _ in range(n)]
heap = [(0,a)]
while heap:
c,x = hq.heappop(heap)
for nx in graph[x]:
if (x,nx) not in shortcut:
nc = c + edges[x][nx]
if nc < visit[nx]:
visit[nx] = nc
trace[nx] = [x]
if nx != end:
hq.heappush(heap,(nc,nx))
elif nc == visit[nx]:
trace[nx].append(x)
return visit,trace
- trace를 2차원 array로 만들어줘서 trace[x] = [px1,px2 ...] x로 들어오는 prev x를 모두 저장한다. (최소 코스트로 들어오는 경우에만)
2. DFS
def dfs(x):
global T,shortcut,start,D
if D[x] or x == start:
return
D[x] = True
for px in T[x]:
shortcut.add((px,x))
dfs(px)
- T는 다익스트라에서 얻은 2차원 trace, shortcut은 set으로 저장한 최단경로들.
- D는 재귀에서 재방문 했을 때, 방문체크를 하는 배열
- start,end는 전역변수로 시작할 때 받았던 node
- 재귀를 통해서 shortcut을 모두 찾아준다.
3. 입력/출력
while True:
n,m = map(int,input().split())
if (n,m) == (0,0):
break
start,end = map(int,input().split())
graph = [[] for _ in range(n)]
edges = [[inf]*n for _ in range(n)]
shortcut = set()
for _ in range(m):
x,nx,cost = map(int,input().split())
graph[x].append(nx)
edges[x][nx] = cost
V,T = di(start,end)
if not V[end]:
print(-1)
else:
D = [False]*n
dfs(end)
V,T = di(start,end)
if V[end] == inf:
print(-1)
else:
print(V[end])
- graph와 edges 정보를 모두 받아준 후, 다익스트라를 돌린다.
- 다익스트라를 돌린 후, end node에 도달하지 못하면 바로 -1
- end node에 도달한 경우, dfs를 돌려서 shortcut을 모두 제거한다.
- 이후 다시 다익스트라를 돌려서 도달할 수 있으면 최소 비용 V[end]를, 그렇지 못하면 -1을 출력한다.
전체코드
import heapq as hq
import sys
input = lambda : sys.stdin.readline().rstrip()
inf = 1e9
def di(a,b):
visit = [inf]*n
visit[a] = 0
trace = [[] for _ in range(n)]
heap = [(0,a)]
while heap:
c,x = hq.heappop(heap)
for nx in graph[x]:
if (x,nx) not in shortcut:
nc = c + edges[x][nx]
if nc < visit[nx]:
visit[nx] = nc
trace[nx] = [x]
if nx != end:
hq.heappush(heap,(nc,nx))
elif nc == visit[nx]:
trace[nx].append(x)
return visit,trace
def dfs(x):
global T,shortcut,start,D
if D[x] or x == start:
return
D[x] = True
for px in T[x]:
shortcut.add((px,x))
dfs(px)
while True:
n,m = map(int,input().split())
if (n,m) == (0,0):
break
start,end = map(int,input().split())
graph = [[] for _ in range(n)]
edges = [[inf]*n for _ in range(n)]
shortcut = set()
for _ in range(m):
x,nx,cost = map(int,input().split())
graph[x].append(nx)
edges[x][nx] = cost
V,T = di(start,end)
if not V[end]:
print(-1)
else:
D = [False]*n
dfs(end)
V,T = di(start,end)
if V[end] == inf:
print(-1)
else:
print(V[end])
기존 코드
import heapq as hq
import sys
input = lambda : sys.stdin.readline().rstrip()
inf = 1e9
def di(a,b):
visit = [inf]*n
visit[a] = 0
visit[end] = []
trace = [-1]*n
trace[end] = []
heap = [(0,a)]
while heap:
c,x = hq.heappop(heap)
for nx in graph[x]:
if (x,nx) not in shortcut:
nc = c + edges[x][nx]
if nx != end:
if nc < visit[nx]:
hq.heappush(heap,(nc,nx))
visit[nx] = nc
trace[nx] = x
else:
visit[end].append(nc)
trace[end].append(x)
return visit,trace
while True:
n,m = map(int,input().split())
if (n,m) == (0,0):
break
start,end = map(int,input().split())
graph = [[] for _ in range(n)]
edges = [[inf]*n for _ in range(n)]
shortcut = set()
for _ in range(m):
x,nx,cost = map(int,input().split())
graph[x].append(nx)
edges[x][nx] = cost
V,T = di(start,end)
if not V[end]:
print(-1)
else:
min_cost = min(V[end])
temp = [idx for idx,val in enumerate(V[end]) if val == min_cost]
for idx in temp:
x,px = end,T[end][idx]
shortcut.add((px,x))
while px != start:
x = px
px = T[px]
shortcut.add((px,x))
V,T = di(start,end)
if not V[end]:
print(-1)
else:
print(min(V[end]))
코멘트
엣지 케이스라기엔 뭔가 당연했는데 너무 생각없이 풀었다.
그래도 디버깅 방향성 잘 잡았고, 재귀에서 시간초과 발생하는거 바로 방문체크 안한거 생각해서 바로 고쳤음.
'Algorithm > Graph' 카테고리의 다른 글
[파이썬] 백준 23034 : 조별과제 멈춰 (플레5) (0) | 2023.12.22 |
---|---|
[파이썬] 백준 1944 : 복제 로봇 (골드1) (0) | 2023.12.19 |
[파이썬] 백준 1194: 달이 차오른다, 가자. (골드1) (0) | 2023.12.19 |
[파이썬] 백준 2021 : 최소 환승 경로 (골드2) (0) | 2023.12.17 |
[파이썬] 백준 5213 : 과외맨 (골드2) (0) | 2023.12.17 |
[파이썬] 백준 16441 : 아기돼지와 늑대 (골드3) (0) | 2023.12.17 |
[파이썬] 백준 2887 : 행성터널 (플레5) (0) | 2023.12.16 |
댓글