코틀린 코딩테스트 강좌, 최소 신장 트리

1. 최소 신장 트리란?

최소 신장 트리(Minimum Spanning Tree, MST)는 가중치가 부여된 그래프에서 모든 정점을 포함하면서 가중치의 합이 최소가 되도록 하는 부분 그래프를 말합니다. 즉, 트리의 모든 정점이 연결되어 있으며 주어진 간선의 가중치의 합이 가장 작은 트리를 의미합니다. 최소 신장 트리는 네트워크 설계, 도로 건설, 통신망 구축 등 다양한 분야에서 활용됩니다.

2. 알고리즘 문제 제시

다음은 최소 신장 트리를 구하는 문제입니다:

문제 설명

주어진 정점 수 V와 간선 수 E가 있는 그래프가 주어질 때, 최소 신장 트리의 가중치의 합을 구하는 프로그램을 코틀린으로 작성하세요. 간선은 가중치와 함께 주어집니다.

입력 형식

  • 첫 번째 줄에 정점 수 V와 간선 수 E가 주어진다.
  • 다음 E개의 줄에는 각 간선의 정보 u, v, w (정점 u와 v를 연결하는 간선의 가중치 w)가 주어진다.

출력 형식

최소 신장 트리의 가중치의 합을 출력한다.

3. 문제 해결 방법론

3.1. 알고리즘 선택

최소 신장 트리를 구하는 방법으로는 Prim 알고리즘과 Kruskal 알고리즘이 있습니다. 본 강좌에서는 Prim 알고리즘을 사용하여 문제를 해결하겠습니다. Prim 알고리즘은 다음과 같은 절차로 진행됩니다.

  1. 임의의 정점에서 시작하여 연결된 간선 중 가장 가중치가 작은 간선을 선택하여 트리에 포함시킵니다.
  2. 트리에 포함된 정점과 연결된 모든 간선 중에서 가중치가 가장 작은 간선을 선택합니다.
  3. 이 과정을 모든 정점이 트리에 포함될 때까지 반복합니다.

3.2. 시간 복잡도

Prim 알고리즘의 시간 복잡도는 O(E log V)입니다. 여기서 E는 간선의 수, V는 정점의 수입니다. 반면, Kruskal 알고리즘은 O(E log E)로 비슷하지만 서로 다른 경우에 더 효율적일 수 있습니다.

4. 코틀린 코드 구현

아래는 Prim 알고리즘을 사용한 최소 신장 트리의 가중치 합을 구하는 코틀린 코드입니다:


fun findMinimumSpanningTree(V: Int, edges: List>): Int {
    val graph = Array(V) { mutableListOf>() }
    for (edge in edges) {
        graph[edge.first].add(Pair(edge.second, edge.third))
        graph[edge.second].add(Pair(edge.first, edge.third))
    }

    val visited = BooleanArray(V)
    val minHeap = PriorityQueue>(compareBy { it.second })
    minHeap.add(Pair(0, 0)) // (정점, 가중치)
    var totalWeight = 0

    while (minHeap.isNotEmpty()) {
        val (u, weight) = minHeap.poll()
        if (visited[u]) continue
        visited[u] = true
        totalWeight += weight

        for ((v, w) in graph[u]) {
            if (!visited[v]) {
                minHeap.add(Pair(v, w))
            }
        }
    }

    return totalWeight
}
            

4.1. 메인 함수와 입력 부분

위의 `findMinimumSpanningTree` 함수를 사용할 메인 함수와 입력 처리 부분은 다음과 같습니다:


fun main() {
    val (V, E) = readLine()!!.split(" ").map { it.toInt() }
    val edges = mutableListOf>()

    repeat(E) {
        val edge = readLine()!!.split(" ").map { it.toInt() }
        edges.add(Triple(edge[0], edge[1], edge[2]))
    }

    val result = findMinimumSpanningTree(V, edges)
    println(result)
}
            

5. 문제 풀이 과정에서의 주의 사항

최소 신장 트리를 구하는 과정에서 몇 가지 주의해야 할 사항들이 있습니다:

  • 그래프의 정점은 인덱스 0부터 시작하는 경우가 많으므로 입력할 때 주의해야 합니다.
  • 모든 정점을 방문해야 하므로, visited 배열의 초기화를 잊지 않도록 합니다.
  • 모든 간선의 가중치가 같은 경우에도 알고리즘을 잘 수행할 수 있도록 설계해야 합니다.
  • 최종적으로 출력되는 가중치의 합이 올바른지 확인합니다.
  • 간선이 중복되지 않도록 처리해야 합니다.

6. 예제 테스트 케이스

다음은 최소 신장 트리 문제를 테스트하기 위한 예제 케이스입니다:

입력 예시

4 5
0 1 1
0 2 3
1 2 1
1 3 4
2 3 1

출력 예시

3

예제 입력은 4개의 정점과 5개의 간선으로 이루어진 그래프입니다. 최소 신장 트리를 구성하는 간선의 가중치의 합은 3이어야 합니다.

7. 결론

이번 강좌에서는 코틀린을 사용하여 최소 신장 트리를 구하는 Prim 알고리즘을 구현해보았습니다. 이 알고리즘은 효율적이며 구현하기도 쉬워 다양한 문제에 적용할 수 있습니다. 알고리즘 문제 풀이 과정에서 그래프의 구조를 이해하고 최적의 방식으로 문제를 해결하는 능력을 키우는 것이 중요합니다. 앞으로도 다양한 알고리즘 문제를 통해 실력을 향상시켜 나가길 바랍니다.