본문 바로가기

카테고리 없음

프로그래머스 - 배달

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

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

문제 설명
N개의 마을로 이루어진 나라가 있습니다. 이 나라의 각 마을에는 1부터 N까지의 번호가 각각 하나씩 부여되어 있습니다. 각 마을은 양방향으로 통행할 수 있는 도로로 연결되어 있는데, 서로 다른 마을 간에 이동할 때는 이 도로를 지나야 합니다. 도로를 지날 때 걸리는 시간은 도로별로 다릅니다. 현재 1번 마을에 있는 음식점에서 각 마을로 음식 배달을 하려고 합니다. 각 마을로부터 음식 주문을 받으려고 하는데, N개의 마을 중에서 K 시간 이하로 배달이 가능한 마을에서만 주문을 받으려고 합니다. 다음은 N = 5, K = 3인 경우의 예시입니다.


위 그림에서 1번 마을에 있는 음식점은 [1, 2, 4, 5] 번 마을까지는 3 이하의 시간에 배달할 수 있습니다. 그러나 3번 마을까지는 3시간 이내로 배달할 수 있는 경로가 없으므로 3번 마을에서는 주문을 받지 않습니다. 따라서 1번 마을에 있는 음식점이 배달 주문을 받을 수 있는 마을은 4개가 됩니다.
마을의 개수 N, 각 마을을 연결하는 도로의 정보 road, 음식 배달이 가능한 시간 K가 매개변수로 주어질 때, 음식 주문을 받을 수 있는 마을의 개수를 return 하도록 solution 함수를 완성해주세요.

제한사항
마을의 개수 N은 1 이상 50 이하의 자연수입니다.
road의 길이(도로 정보의 개수)는 1 이상 2,000 이하입니다.
road의 각 원소는 마을을 연결하고 있는 각 도로의 정보를 나타냅니다.
road는 길이가 3인 배열이며, 순서대로 (a, b, c)를 나타냅니다.
a, b(1 ≤ a, b ≤ N, a != b)는 도로가 연결하는 두 마을의 번호이며, c(1 ≤ c ≤ 10,000, c는 자연수)는 도로를 지나는데 걸리는 시간입니다.
두 마을 a, b를 연결하는 도로는 여러 개가 있을 수 있습니다.
한 도로의 정보가 여러 번 중복해서 주어지지 않습니다.
K는 음식 배달이 가능한 시간을 나타내며, 1 이상 500,000 이하입니다.
임의의 두 마을간에 항상 이동 가능한 경로가 존재합니다.
1번 마을에 있는 음식점이 K 이하의 시간에 배달이 가능한 마을의 개수를 return 하면 됩니다.

 

 

다익스트라 알고리즘에 관련된 문제, 해당 개념에 대한 복습이 될 것같다.

 

def solution(N, road, K):
    answer = 0
    road_dict = {i: {} for i in range(1, N + 1)}
    
    for village, connect, distance in road:
        if connect in road_dict[village]:
            road_dict[village][connect] = min(distance, road_dict[village][connect])
            road_dict[connect][village] = min(distance, road_dict[connect][village])
        else:
            road_dict[village][connect] = distance
            road_dict[connect][village] = distance

가장 먼저 한 것은 road라는 이름으로 주어진 리스트 형태의 지도를 딕셔너리 형태로 변환하는 것이다.

마을의 수 만큼의 키를 가지는 딕셔너리를 생성하고 각 마을과 다른 마을을 연결하는 간선과 거리를 표시하기 위해 각 벨류는 다시 딕셔너리 형태로 표시하도록한다.

그 후 첫번째 반복문을 통해 각 마을과 연결된 다른 마을과 거리를 표시하도록 한다. 해당 마을과의 거리가 입력이 되어있지 않을 경우 현재 거리를 입력하고 이미 입력이 되어있을 경우 이미 입력된 거리와 비교하여 더 짧은 쪽을 입력하도록 한다.

 

def dijkstra(graph, start=1):
    distances = {node: float('inf') for node in graph}
    distances[start] = 0
    
    explore = [(0, start)]
    
    while explore:
        distance, location = explore.pop(0)
        
        if distance > distances[location]:
            continue
        
        for node, node_distance in graph[location].items():
            expected_distance = node_distance + distance
            
            if expected_distance < distances[node]:
                distances[node] = expected_distance
                explore.append((expected_distance, node))
    
    return distances

이번에는 다익스트라 함수를 작성한다. 해당 문제에서 출발 위치는 항상 1이므로 start는 1로 고정한 후 각 거리들을 입력하기 위한 distances 딕셔너리를 작성한다. 그리고 시작점과 시작점의 거리는 항상 0이므로 distance[start] = 0으로 지정해준다.

 

explore라는 리스트에 탐색하는 노드의 정보를 입력한다. 현재까지의 거리와 현재 위치를 지속적으로 입력해서 현재까지의 최단거리를 기록하고 동시에 현재 위치를 기록하여 탐색을 한다.

explore라는 리스트에서 원소를 하나씩 꺼내서 해당 위치에서 탐색을 하도록 한다. 이때, 현재 탐색중인 노드까지의 거리가 distances의 현재 노드까지의 거리보다 클 경우 이미 탐색한 노드이거나 최적화된 거리가 아니라는 의미이므로 탐색하지 않고 다음 노드를 탐색하도록 진행한다.

 

그 후 현재 탐색중인 노드와 연결된 다른 노드들을 전부 탐색하여 탐색 노드의 위치를 이동하고 거리를 반영하여 explore에 입력해서 탐색을 계속하도록 한다. 이때, expected_distance에는 탐색할 노드까지의 예상 거리가 표시되는데 distance에 입력되어있는 현재 노드까지의 현재까지의 최단거리보다 길 경우에는 최적화된 거리가 아니라는 의미이므로 탐색하지 않는다.

 

# def dijkstra(graph, start=1):
#     distances = {node: float('inf') for node in graph}
#     distances[start] = 0
    
#     explore = [(0, start)]
    
#     while explore:
#         distance, location = explore.pop(0)
        
#         if distance > distances[location]:
#             continue
        
#         for node, node_distance in graph[location].items():
#             expected_distance = node_distance + distance
            
#             if expected_distance < distances[node]:
#                 distances[node] = expected_distance
#                 explore.append((expected_distance, node))
    
#     return distances

# def solution(N, road, K):
#     answer = 0
#     road_dict = {i: {} for i in range(1, N + 1)}
    
#     for village, connect, distance in road:
#         if connect in road_dict[village]:
#             road_dict[village][connect] = min(distance, road_dict[village][connect])
#             road_dict[connect][village] = min(distance, road_dict[connect][village])
#         else:
#             road_dict[village][connect] = distance
#             road_dict[connect][village] = distance
        
    distances = dijkstra(road_dict)
    
    for distance in distances.values():
        if distance <= K:
            answer += 1
        
    return answer

 

위의 방법으로 1번 노드(마을)에서 각 마을까지의 최단거리를 저장하고 있는 distances라는 딕셔너리를 반환 할 수 있다.

 

마지막으로 distances에서 문제에서 정의한 K값보다 짧은 거리를 가진 모든 노드의 수를 세면 정답을 반환할 수 있다.

 

def dijkstra(graph, start=1):
    distances = {node: float('inf') for node in graph}
    distances[start] = 0
    
    explore = [(0, start)]
    
    while explore:
        distance, location = explore.pop(0)
        
        if distance > distances[location]:
            continue
        
        for node, node_distance in graph[location].items():
            expected_distance = node_distance + distance
            
            if expected_distance < distances[node]:
                distances[node] = expected_distance
                explore.append((expected_distance, node))
    
    return distances

def solution(N, road, K):
    answer = 0
    road_dict = {i: {} for i in range(1, N + 1)}
    
    for village, connect, distance in road:
        if connect in road_dict[village]:
            road_dict[village][connect] = min(distance, road_dict[village][connect])
            road_dict[connect][village] = min(distance, road_dict[connect][village])
        else:
            road_dict[village][connect] = distance
            road_dict[connect][village] = distance
        
    distances = dijkstra(road_dict)
    
    for distance in distances.values():
        if distance <= K:
            answer += 1
        
    return answer

전체코드

 

다익스트라 알고리즘은 아직 숙련도가 부족하다는 느낌이 들었다.