문제
트리의 지름이란, 트리에서 임의의 두 점 사이의 거리 중 가장 긴 것을 말한다. 트리의 지름을 구하는 프로그램을 작성하시오.
입력
트리가 입력으로 주어진다. 먼저 첫 번째 줄에서는 트리의 정점의 개수 V가 주어지고 (2 ≤ V ≤ 100,000)둘째 줄부터 V개의 줄에 걸쳐 간선의 정보가 다음과 같이 주어진다. 정점 번호는 1부터 V까지 매겨져 있다.
먼저 정점 번호가 주어지고, 이어서 연결된 간선의 정보를 의미하는 정수가 두 개씩 주어지는데, 하나는 정점번호, 다른 하나는 그 정점까지의 거리이다. 예를 들어 네 번째 줄의 경우 정점 3은 정점 1과 거리가 2인 간선으로 연결되어 있고, 정점 4와는 거리가 3인 간선으로 연결되어 있는 것을 보여준다. 각 줄의 마지막에는 -1이 입력으로 주어진다. 주어지는 거리는 모두 10,000 이하의 자연수이다.
출력
첫째 줄에 트리의 지름을 출력한다.
문제 링크
풀이
백준 1967번 문제와 동일한 문제이며 풀이도 동일하다.▼
먼저 트리의 지름은 '트리에서 어떤 두 노드를 선택해서 양쪽으로 쫙 당길 때, 가장 길게 늘어나는 경우가 있을 것이다. 이럴 때 트리의 모든 노드들은 이 두 노드를 지름의 끝 점으로 하는 원 안에 들어가게 된다.'
이라는 설명을 통해 소개가 되었는데, 잘 생각 해 보면 저기서 말하는 어떤 두 노드
는 가장 하위 레벨의 노드밖에 해당되지 않는다.▼
하위 노드가 아닌 중간이나 상위 노드를 고르게 되면, 원 안에 전부 들어가지 않게 된다.▼
그래서 우리가 해야 할 일은 가장 하위 노드를 구하는 것이다.
프로그램 동작
프로그램은 하위 노드를 구하는 것을 기준으로 돌아간다.
그런데 이렇게만 말하면 의문이 들 수 있다.
'하위 노드가 몇 개나 될 줄 알고 전부 구한다는 거지? 아니 그리고 여러 개면 그 중 뭘 고를건데?'
하위 노드가 여러 개라면 그걸 찾는데만 시간이 엄청나게 걸릴 것이다.
그래서 우리는 루트 노드를 기준으로 가장 긴 길이를 가질 수 있는 하위 노드를 구할 것이다.
1. 첫번째로는 노드들의 정보를 담기 위해 2차원 배열을 선언한다. 2차원 배열인데 튜플을 곁들인 2차원 튜플 배열이다.
튜플의 내용은 (노드 인덱스, 길이) 이다.▼
2. 노드 정보가 들어오면 양 쪽 노드에 각각 값을 넣어준다.▼
값을 다 넣을 때 까지 반복해준다.▼
3. 이제 dfs를 통해 가장 긴 길이가 나오는 최하위 노드를 찾아준다.
노드 1, 길이 0 부터 시작한다.▼
노드 1의 방문 여부를 true로 바꿔주고, 노드 1의 원소를 순회한다.
노드 2에는 방문한 적이 없으므로 노드 2를 다음 방문 노드로 정하고, length에 노드 1과 2 사이의 거리인 3을 더해준다.▼
노드 2로 가서 노드 2의 방문 여부를 true로 바꿔주고 노드 2의 원소를 순회한다.
그런데 1은 이미 방문했으므로 스킵하고, 노드 4로 이동한다.
노드 4와 노드 2 사이의 거리는 5 이므로, 다음에는 노드 4와 length 3+5로 함수를 실행한다.▼
노드 4의 방문 여부는 true로 바꾸고, 노드 4의 원소를 순회한다.
이때 2는 방문했으므로 바로 7로 옮긴다.
다음 함수 실행은 노드 7, 길이는 8 + 1이다.
하지만 노드 7의 원소는 이미 다 방문했던 노드들이므로 깊이 끝까지 들어온 셈이다.
그러므로 노드 1에서 방문하지 않았던 (3, 2) 원소를 방문하러 간다.▼
앞의 과정들을 반복해주자.▼
과정을 반복해주고 나면 이렇게 결과가 나오는데 가장 긴 값은 5번 노드쪽으로 갔을 때이다.
그러므로 가장 긴 길이를 가진 끝 값은 5번으로 기록을 해둔다.
4. visit과 length를 전부 초기화 시키고 5번을 시작으로 다시 탐색을 시작한다.▼
다시 탐색을 쭉 해주고 나면, ▼
갈래가 두 갈래라 두 종류의 답이 나오는데 그 중 가장 큰 값이 답이 된다.
그림을 그려서 확인해보면 확실하게 답이 맞음을 알 수 있다.▼
Swift 코드
let N = Int(readLine()!)!
var tree = Array(repeating: [(Int, Int)](), count: N + 1)
var visit = Array(repeating: false, count: N + 1)
var answer = 0
var endNode = 0
for _ in 0..<N {
let tmp = readLine()!.split(separator: " ").map { Int(String($0))! }
for i in 1...(tmp.count - 2) / 2 {
tree[tmp[0]].append((tmp[i * 2 - 1], tmp[i * 2]))
}
}
func getDistance(_ node: Int, _ length: Int) {
if visit[node] {
return
}
visit[node] = true
if answer < length {
answer = length
endNode = node
}
for i in 0..<tree[node].count {
getDistance(tree[node][i].0, length + tree[node][i].1)
}
}
getDistance(1, 0)
answer = 0
visit = Array(repeating: false, count: N + 1)
getDistance(endNode, 0)
print(answer)
1967번 문제에서 만든 함수를 그대로 써도 통과가 될 정도로 똑같은 문제다.
둘의 차이점은 입력을 어떻게 받냐인데, 입력을 어떻게 받는지 정도는 브론즈 문제나 실버 문제를 어느정도 풀었다면 금방 생각해낼 수 있을 것이다.
'Algorithm > BOJ PS' 카테고리의 다른 글
백준 1300번 K번째 수 - SWIFT (0) | 2024.02.26 |
---|---|
백준 1181번 단어 정렬 - SWIFT (0) | 2024.02.25 |
백준 1030번 프랙탈 평면 - C++ (0) | 2024.02.18 |
백준 1065번 한수 - SWIFT, C++ (0) | 2024.02.18 |
백준 1152번 단어의 개수 - SWIFT, C++ (0) | 2024.02.13 |