743 네트워크 딜레이 타임

5 minute read

743 Network Delay Time

문제

You are given a network of n nodes, labeled from 1 to n.
You are also given times, a list of travel times as directed edges times[i] = (ui, vi, wi), where ui is the source node, vi is the target node, and wi is the time it takes for a signal to travel from source to target.
We will send a signal from a given node k.
Return the time it takes for all the n nodes to receive the signal. If it is impossible for all the n nodes to receive the signal, return -1.

  • times[i][0] = ui = 시작 노드
  • times[i][1] = vi = 타겟 노드
  • times[i][2] = wi = 소요 시간
  • k: 시작 노드
  • n: 노드의 수
  • -1: 불가능할 경우

조건

  • 1 <= k <= n <= 100
  • 1 <= times.length <= 6000
  • times[i].length == 3
  • 1 <= ui, vi <= n
  • ui != vi
  • 0 <= wi <= 100
  • All the pairs (ui, vi) are unique. (i.e., no multiple edges.)

예제

Input: times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2
Output: 2

Input: times = [[1,2,1]], n = 2, k = 1
Output: 1

Input: times = [[1,2,1]], n = 2, k = 2
Output: -1

해결

1st

1 생각

  • 방향이 있는 그래프.
    • 연결이 안 되어 있을 수도 있다
    • 연결되어 있어도 방향이 안 맞을 수 있다
    • 모든 노드에 도달할 수 있어야 한다
  • k에서 시작해서 연결된 노드들에서 신호를 받을 수 있는 시간
    • 총합? 은 아니고
    • 도달할 수 있는 노드까지의 최단 시간을 구하면 된다
    • [[2,1,1],[2,3,1],[3,4,1]]에서, 2에서 시작하면 그 다음 1, 3으로 갈 수 있고, 3에서는 다시 4로 갈 수 있다

1 코드

def first(self, times: List[List[int]], n: int, k: int) -> int:
    result = [None] * n
    # k를 찾는다
    loop_cnt = 0
    queue = [k]
    while queue and loop_cnt < n:
        node_curr = queue.pop(0)
        for node in times:
            if node[0] == node_curr:
                queue.append(node[1])
                if result[loop_cnt]:                        
                    result[loop_cnt].append(node[2])
                else:
                    result[loop_cnt] = [node[2]]
                
        loop_cnt += 1
    ans = 0
    print(result)
    for values in result:
        if values is not None:
            ans += max(values)

    if ans == 0:
        return -1
    else:
        return ans

1 결과: 실패

times=[
  [1, 2, 1],
  [2, 1, 3]
]
print(s.first(times, 2, 2)) # Expected: 3
  • 처음 2 -> 1에서 모든 노드에 대한 시그널 전파 완료됐으므로, 결과는 3이어야 한다
  • 하지만 1 코드에서는 2 -> 1, 1 -> 2까지 카운트 하므로 4가 나오면서 실패
times=[
  [1,2,1],
  [2,3,2],
  [1,3,4]
]
print(s.first(times, 3, 1))  # Expected: 3

2nd

2 생각

  • 방문했는지 여부를 체크하면 되지 않을까?
다익스트라?
def Dijkstra(Graph, source):
  dist[source] = 0
  # create vertex priotiry queue Q
  Q = []

  for v in Graph:
    if v != source:
      dist[v] = INFINITY
      prev[v] = UNDEFINED
    Q.add_with_priority(v, dist[v])
  
  while Q:
    u = Q.extract_min()
    for neighbor in u:
      alt = dist[u] + length(u, neighbor) # `u`까지의 거리에 `u`와 `v` 간의 거리를 더한다
      if alt < dist[neighbor]:  # 새로 구한 값이 기존 인접 노드까지의 거리보다 작다면
        # 값과 이전 노드를 바꾼다
        dist[neighbor] = alt
        prev[neighbor] = u
        Q.decrease_priority(neighbor, alt)
heapq?

2 코드

from typing import *

def second(self, times: List[List[int]], n: int, k: int) -> int:
    graph = {}
    # 인접 리스트 생성
    for node, node_next, time in times:
        # 소요 시간을 계속 추적해야 하므로, tuple로 관리
        if node in graph:
            graph[node].append((node_next, time))
        else:
            graph[node] = [(node_next, time)]
    # print(graph)
    
    loop_cnt = 0
    # 인접한 노드들을 탐색하기 위한 큐
    # (k까지, 소요되는 시간) 튜플로 저장
    queue = [(k, 0)]
    # ~까지의 시간을 저장하기 위한 딕셔너리
    time_to = {k:0} # 시작 지점의 시간은 처음에 셋팅해 둔다
    while queue:
        # 현재 정점
        node_curr = queue.pop(0)
        vertex_curr = node_curr[0]
        # 현재 정점 방문 표시
        time_accumulative = node_curr[1]

        if vertex_curr in graph:
            for node_next in graph[vertex_curr]:
                # 인접 노드
                vertex_next = node_next[0]
                # 방문한 적이 있어도, 축적된 값이 더 작으면 그 값으로 치환할 수 있어야 한다
                # 그렇다면 "다음 정점이 다시 이전 정점을 가리키면서 무한 반복하는 것 방지"은 어떻게?
                time_next = node_next[1]
                # 시간을 축적해 간다
                time_accumulative_new = time_accumulative + time_next

                if vertex_next in time_to:
                    # 새로 축적된 시간이 기존 시간보다 작으면 더 작은 값으로 바꾼다
                    if time_accumulative_new < time_to[vertex_next]:
                        time_to[vertex_next] = time_accumulative_new
                        # 더 적은 시간이 발견되면 해당 노드 탐색하도록 추가한다
                        queue.append((vertex_next, time_accumulative_new))
                else:
                    # 새로 축적된 시간없으면 저장
                    time_to[vertex_next] = time_accumulative_new
                    # 신규 노드 탐색하도록 추가한다
                    queue.append((vertex_next, time_accumulative_new))
    # print(time_to)
    if len(time_to) == n:
        return max(time_to.values())
    else:
        return -1

s = Solution()
case1 = [[2, 1, 1],[2, 3, 1],[3, 4, 1]]
case2 = [[1,2,1],[2,1,3]]
case3 = [[1,2,1],[2,3,2],[1,3,2]]
case4 = [[1,2,1],[2,3,2],[1,3,4]]
case5 = [[3, 1, 5],[3, 2, 2],[2, 1, 2],[3, 4, 1],[4, 5, 1],[5, 6, 1],[6, 7, 1],[7, 8, 1],[8, 1, 1]]
case6 = [[1,2,1],[2,1,3]]
case7 = [[1,2,1],[2,3,7],[1,3,4],[2,1,2]]
print(s.second(case1, 4, 2)) # expected: 2
print(s.second(case3, 3, 1)) # expected: 2
print(s.second(case3, 3, 2)) # expected: -1
print(s.second(case4, 3, 1)) # expected: 3
print(s.second(case5, 8, 3)) # expected: 5
print(s.second(case6, 2, 2)) # expected: 3
print(s.second(case7, 3, 2)) # expected: 6

2 결과: 성공

52 / 52 test cases passed.
Status: Accepted
Runtime: 492 ms
Memory Usage: 16.3 MB

3rd

책의 코드

def answer(self, times: List[List[int]], n: int, k: int) -> int:
    graph = collections.defaultdict(list)
    """ 
    그래프 인접 리스트 구성
    {
        3: [(1, 5), (2, 2), (4, 1)], 
        2: [(1, 2)], 
        4: [(5, 1)], 
        5: [(6, 1)], 
        6: [(7, 1)], 
        7: [(8, 1)], 
        8: [(1, 1)]
    }
    """
    for u, v, w in times:
        graph[u].append((v, w))
    
    # 시작 노드
    Q = [(0, k)]
    distance = collections.defaultdict(list)
    
    # BFS지만, 최소 힙을 사용하여 시간이 작은 값부터 확인한다
    while Q:
        # https://docs.python.org/3/library/heapq.html
        # time이 가장 작은 원소를 꺼낸다
        time, node = heapq.heappop(Q)
        print('pop: ', (time, node))
        if node not in distance:
            # node까지의 거리에 소요되는 시간
            distance[node] = time
            for v, w in graph[node]:
                alt = time + w
                print('push: ', (alt, v))
                # https://docs.python.org/3/library/heapq.html#basic-examples
                # 첫번째 인자 시간(alt)을 최소값으로
                heapq.heappush(Q, (alt, v))
            print('distance: ', distance)
    
    # 모든 노드 방문했는지 여부 확인
    if len(distance) == n:
        return max(distance.values())
    else:
        return -1

4th

Dijkstra 알고리즘대로 구현

Updated: