강의로 돌아가기
홍희표

[Kotlin] 뭐가 잘못됬는지 모르겠어요 TC 2,8,9,10,12,13

root변수를 first말고 .get메소드로 0~20 인덱스를 바꿔가며 테스트해보니, TC 2, 9, 10, 12가 통과가 되네요..
이러면 root를 잘못설정한거 같은데.. 간선 또는 연결하고 있는 child가 가장 많은게 root로 설정하는게 잘못되었나요?
아니면 다른 로직에서 틀린게 있다면 지적 부탁드립니다.

작성중인 코드―solution.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import kotlin.math.abs

class Solution {
    private lateinit var visited: BooleanArray

    private var parent = 0

    private var root = 0

    private var childMap = mutableMapOf<Int, MutableList<Int>>()

    fun solution(n: Int, wires: Array<IntArray>): Int {
        val array = initArray(n, wires)
        visited = BooleanArray(n) { false }
        root = array.sortedByDescending { it.second.count() }.first().first // 중복되는 숫자가 많은것을 루트로 설정

        for (i in array[root - 1].second) {
            childMap[i] = mutableListOf()
        }
        dfs(array, array[root - 1].second)
        return getResult(childMap.map { it.value.size }.sorted())
    }

    private fun initArray(n: Int, wires: Array<IntArray>): Array<Pair<Int, MutableList<Int>>> {
        val array = Array(n) { it + 1 to mutableListOf<Int>() }

        for (intArray in wires) {
            array[intArray.first() - 1].second.add(intArray.last())
            array[intArray.last() - 1].second.add(intArray.first())
        }
        return array
    }

    private fun dfs(array: Array<Pair<Int, MutableList<Int>>>, second: MutableList<Int>) {
        if (second.size > 1) {
            for (child in second) {
                if (child == root) // 자식이 루트면 건너뛰기
                    continue
                if (!visited[child - 1]) {
                    visited[child - 1] = true

                    if (childMap[child] != null) {
                        parent = child
                    }
                    childMap[parent]?.add(child)
                    dfs(array, array[child - 1].second)
                }
            }
        }
    }

    private fun getResult(sorted: List<Int>): Int {
        val max = sorted.last()
        val other = sorted.dropLast(1).sum()
        return abs((other + 1) - max) // 루트 노드를 제외했기때문에 여기서 루트 노드갯수 1을 추가
    }
}
2 개의 답변
전현서

간단한 조언이지만 연결되어 있는 모든 간선들을 하나씩 전부 끊어봐야합니다.
그리고 끊어진 간선으로부터 이어진 그래프대로 이동해서 몇 번 이동이 가능한지 보면 됩니다.
만약 [1, 2], [2, 3], [4, 2] 의 형태의 그래프가 있다고하면 1과 2사이를 끊어보고, 2와 3사이를 끊어보고, 2와 4사이를 끊어봅니다.
그리고 끊은 간선을 연결하는 노드 둘 중 하나를 임의로 지정하여 몇 개가 이어져있는지 탐색합니다.
만약 1번과 2번을 끊는다고 하면 1번노드 기준으로 탐색을 수행하면 값은 1개 밖에 없으므로 1이 될겁니다. 여기서 2기준으로 탐색하면
자연적으로 3이 되는 것을 알 수 있을겁니다. 애초에 하나의 트리에서 하나만 끊겼으므로 전체 노드수에서 값을 빼기만 하면 되죠
전부 다 수행해서 두 트리 사이의 개수의 차가 제일 적은 것이 가장 개수가 비슷한 것이 되므로 그것이 답이 됩니다.
사실 질문의 내용을 이해하지 못해 제가 아는 것에 한해서만 알려드립니다..(비효율적일 수도 있습니다.. 완전탐색이라서요)

  • 홍희표

    감사합니다 위와 같은 로직으로 하니 해결되네요 거의 정답을 알려주심

    홍희표―2021.10.07 02:21
user188245

일단 보이는 로직이,

  1. 간선이 가장 많은 노드를 선택하고 그 노드를 root로 삼는다
  2. root노드와 직접 연결된 자식노드를 하나씩 선택하며 "모든 후손노드 + 자기자신"을 childMap에 넣는다.
  3. "2."에서 선택한 자식노드들중, 후손노드 개수가 가장 많은 놈을 골라 그 값을 max라 한다. 그것 이외에 나머지 노드의 후손개수의 합을 other로 한다.
  4. abs(other + 1 - max)를 내놓는다.

이것은 결국 이거와 같습니다.

  1. 간선이 가장 많은 노드 Root를 선택한다.
  2. 그 노드에서 직접적으로 노드 중 후손 개수가 가장 많은 노드 Child를 선택한다.
  3. Root와 Child를 끊어버린다.

안타깝게도, root노드와 자식들 / root 노드의 자식들중 후손노드가 가장 많은 노드 로 잘랐을때 Gap이 최소가 나올 수 있고,

root노드와 자식들 / root노드의 자식의 자식들 중 후손노드가 가장 많은 노드 로 잘랐을때 Gap이 최소가 나올 수 있습니다.

반례는 간단합니다.

[[1,2],[1,3],[1,4],[4,5],[5,6],[6,7],[6,8]]

(2)┐.................┌(7)
....(1)-(4)-(5)-(6)
(3)┘.................└(8)

이 경우 4와 5사이를 끊어버리면, 1~4패밀리 5~8패밀리로 정확히 4개:4개로 나누어지게 됩니다. 따라서 결과는 0.

그러나 4역시 root가 아니고 5역시 root가 아니기 때문에, 4와 5사이 간선은 끊어지지 않습니다. 따라서, 위 로직에 따라 1과 4사이를 끊어버리게 되어, 3:5 결과는 2가 나오게 됩니다.

해결방법은 어쩔 수 없지만, (1) 모든 간선을 다 끊어 보던가, (2) 모든 점을 root로 해서 전부 탐색했을때 최소값을 찾거나 둘중 하나는 적어도 해야 합니다.

  • 홍희표

    오 반례가 명확하네요 다른방법으로 하니 해결했습니다. 제 생각에 허점이 있었네요

    홍희표―2021.10.07 02:22
답변 쓰기
이 입력폼은 마크다운 문법을 지원합니다.