문제 설명
https://www.acmicpc.net/problem/2250
[문제]
이진트리를 다음의 규칙에 따라 행과 열에 번호가 붙어있는 격자 모양의 틀 속에 그리려고 한다. 이때 다음의 규칙에 따라 그리려고 한다.
- 이진트리에서 같은 레벨(level)에 있는 노드는 같은 행에 위치한다.
- 한 열에는 한 노드만 존재한다.
- 임의의 노드의 왼쪽 부트리(left subtree)에 있는 노드들은 해당 노드보다 왼쪽의 열에 위치하고, 오른쪽 부트리(right subtree)에 있는 노드들은 해당 노드보다 오른쪽의 열에 위치한다.
- 노드가 배치된 가장 왼쪽 열과 오른쪽 열 사이엔 아무 노드도 없이 비어있는 열은 없다.
이와 같은 규칙에 따라 이진트리를 그릴 때 각 레벨의 너비는 그 레벨에 할당된 노드 중 가장 오른쪽에 위치한 노드의 열 번호에서 가장 왼쪽에 위치한 노드의 열 번호를 뺀 값 더하기 1로 정의한다. 트리의 레벨은 가장 위쪽에 있는 루트 노드가 1이고 아래로 1씩 증가한다.
아래 그림은 어떤 이진트리를 위의 규칙에 따라 그려 본 것이다. 첫 번째 레벨의 너비는 1, 두 번째 레벨의 너비는 13, 3번째, 4번째 레벨의 너비는 각각 18이고, 5번째 레벨의 너비는 13이며, 그리고 6번째 레벨의 너비는 12이다.
우리는 주어진 이진트리를 위의 규칙에 따라 그릴 때에 너비가 가장 넓은 레벨과 그 레벨의 너비를 계산하려고 한다. 위의 그림의 예에서 너비가 가장 넓은 레벨은 3번째와 4번째로 그 너비는 18이다. 너비가 가장 넓은 레벨이 두 개 이상 있을 때는 번호가 작은 레벨을 답으로 한다. 그러므로 이 예에 대한 답은 레벨은 3이고, 너비는 18이다.
임의의 이진트리가 입력으로 주어질 때 너비가 가장 넓은 레벨과 그 레벨의 너비를 출력하는 프로그램을 작성하시오
[입력]
첫째 줄에 노드의 개수를 나타내는 정수 N(1 ≤ N ≤ 10,000)이 주어진다. 다음 N개의 줄에는 각 줄마다 노드 번호와 해당 노드의 왼쪽 자식 노드와 오른쪽 자식 노드의 번호가 순서대로 주어진다. 노드들의 번호는 1부터 N까지이며, 자식이 없는 경우에는 자식 노드의 번호에 -1이 주어진다.
[출력]
첫째 줄에 너비가 가장 넓은 레벨과 그 레벨의 너비를 순서대로 출력한다. 너비가 가장 넓은 레벨이 두 개 이상 있을 때에는 번호가 작은 레벨을 출력한다.
[예제 입력 1]
19
1 2 3
2 4 5
3 6 7
4 8 -1
5 9 10
6 11 12
7 13 -1
8 -1 -1
9 14 15
10 -1 -1
11 16 -1
12 -1 -1
13 17 -1
14 -1 -1
15 18 -1
16 -1 -1
17 -1 19
18 -1 -1
19 -1 -1
[예제 출력 1]
3 18
풀이
루트 노드를 어떻게 찾느냐와 현재 노드가 문제 속 그림처럼 표 상에 어디에 있는 지를 파악하느냐가 핵심이었던 문제이다. 초반에 트리를 어떻게 문제 속 그림처럼 나타내어 정렬을 할 수 있을 지에 대해 고민만 계속하다 포기했었는데, 정답은 바로 중위 순회에 있었다.
중위 순회를 사용하게 되면 쉽게 현재 트리의 레벨과 전체적인 트리 호출 순서가 문제에서 요구하는 정렬과 맞아 떨어지기 때문에, 레벨 별 가장 왼쪽과 오른쪽 노드의 위치를 알 수 있어 이를 단순히 저장만 하면 후에 레벨 별로 비교만 하면 쉽게 계산이 가능했다.
다른 문제로 루트노드를 찾는 것에 대한 경우 간단하게 해결했는데, 바로 루트 노드는 딱 한 번만 적힌다는 점이었다. 이를 이용하여 전체 정보를 입력받으면서 해당 노드 번호가 입력된 횟수를 적어, 한 번만 불린 노드를 찾으면 그게 루트 노드임을 알 수 있다.
#include <iostream>
#include <cstring>
using namespace std;
int tree[10001][2];//트리 구조 저장
int call[10001] = { 0, };//노드 호출 횟수 저장(루트 노드 파악용)
int mostLeft[10001];//레벨별 가장 왼쪽
int mostRight[10001] = { 0, };//레벨별 가장 오른쪽
void inorder(int node, int level, int& cnt, int& maxLevel) {//중위 순회
if (tree[node][0] != -1)
inorder(tree[node][0], level + 1, cnt, maxLevel);
if (level > maxLevel)//트리 최대 깊이 체크
maxLevel = level;
if (mostLeft[level] > cnt)//자신의 위치가 해당 레벨 가장 왼쪽인지
mostLeft[level] = cnt;
if (mostRight[level] < cnt)//오른쪽인지
mostRight[level] = cnt;
++cnt;
if (tree[node][1] != -1)
inorder(tree[node][1], level + 1, cnt, maxLevel);
}
int main() {
int N;
int parent, child1, child2;
int root = 1;
int ansLevel = 1, ansWidth = 1;
int cnt = 1, maxLevel = 1;
cin >> N;
memset(mostLeft, 10001, sizeof(mostLeft));
for (int i = 0; i < N; i++) {//노드 입력 받기
cin >> parent >> child1 >> child2;
tree[parent][0] = child1;
tree[parent][1] = child2;
++call[parent];
if (child1 != -1)
++call[child1];
if (child2 != -1)
++call[child2];
}
for (int i = 1; i <= N; i++)//루트 노드 탐색
if (call[i] == 1)
root = i;
inorder(root, 1, cnt, maxLevel);//중위 순회
for (int i = 2; i <= maxLevel; i++) //레벨별 너비 확인
if (ansWidth < mostRight[i] - mostLeft[i] + 1) {
ansWidth = mostRight[i] - mostLeft[i] + 1;
ansLevel = i;
}
cout << ansLevel << " " << ansWidth << endl;
return 0;
}
결과
'Algorithm > 코드 풀이' 카테고리의 다른 글
1967번: 트리의 지름 (0) | 2021.06.25 |
---|---|
1194번: 달이 차오른다, 가자. (0) | 2021.05.28 |
1525번: 퍼즐 (0) | 2021.05.19 |
9019번: DSLR (0) | 2021.05.19 |
2146번: 다리 만들기 (0) | 2021.05.12 |