구간 합 구하기 문제와 다른 점은 세그먼트 트리의 갱신 부분에 주의할 부분이 있다는 점이다. 처음에는 단순히 기존값으로 나누고, 새로운 값으로 다시 곱해주면서 리프노드까지 도달하는 방법으로 접근했지만, 예제에서 알 수 있듯이 트리의 값이 0으로 바뀌게 되면 다음 갱신에 문제가 발생한다. 따라서 리프노드부터 먼저 갱신이 이루어지고, 루트노드까지 새로운 값으로 계산을 해주어야 한다.
정답 코드
재귀호출 시에 범위를 반대로 적어 "틀렸습니다"를 연발했다.. 아는 유형의 문제라 방심했던 탓이다. 항상 집중해서 문제를 풀 것.
세그먼트 트리란 구간합을 이진트리의 형태로 구현한 자료구조이다. 부모노드에는 두 자식노드의 총합이 담기며, 두 자식노드는 중간을 기준으로 좌측 범위의 총합, 우측 범위의 총합의 형태로 나뉜다. 그림으로 보는 것이 이해하기 더 쉬울 것이다.
크기 5인 배열의 세그먼트 트리의 구조다. 각 배열의 값이 리프노드에 담기며, 부모 노드로 구간 합을 계산하여 루트에는 배열의 총합이 담기게 된다.
예제 [1,2,3,4,5] 배열의 세그먼트 트리를 그려보면 다음과 같이 만들어진다. 세그먼트 트리의 경우 구간합의 변경에 굉장히 용이하다. 그 이유는 바로 트리구조에서 오는 접근성 때문인데, 부모 노드를 기준으로 두 배를 하면 좌측 자식 노드, 거기에 1을 더하면 우측 자식 노드에 접근이 가능하기 때문이다. 따라서 루트 노드는 0이 아닌 1부터 시작하며 기본적으로 해당 구조의 재귀호출을 통해 구간합의 변경과 조회가 이루어진다.
1번 ~ 4번 인덱스까지의 구간합을 구하게 된다면 좌측 그림과 같이 해당 구간에 접근하여 계산이 이루어진다. 사실 누적합을 이용해서 푼다면 O(1)만에 계산이 가능하지만, 자료의 변경이 일어난다면 처음부터 누적합을 다시 구해야 하기에, 결국 자료의 크기가 N일 때 O(N)시간이 걸리게 된다. 세그먼트 트리의 경우 우측 그림과 같이 조회뿐만 아니라 루트에서 해당 구간 만의 접근을 통해 변경이 가능하기에 O(logN)시간만에 해결이 가능하게 된다. 해당 문제의 경우 구간의 변경이 빈번하게 이루어지기에 세그먼트 트리의 구현을 요구하는 문제인 것이다.
모눈종이의 가장자리에는 치즈가 없다. 즉 (0,0) 좌표부터 너비우선탐색을 수행하여 치즈를 마주치면 해당 좌표를 저장. 탐색도중 같은 좌표를 한번 더 마주하게 된다면 해당 치즈는 2변이 실내온도에 닿은 것으로 판정하여 녹은 것으로 판정한다. 좌표를 저장하고 탐색하는 데에 O(1)만에 탐색이 가능한 Set 자료구조를 사용하였다. 치즈의 개수가 0이 될 때까지 탐색을 수행. 반복이 일어난 횟수를 출력하면 된다.
입력되는 간선의 최대개수가 100,000개, 노드의 개수가 1,000개 이기에 우선순위 큐가 필수이며 메모리 초과를 유도하는 테스트 케이스가 주어지기에 다익스트라 알고리즘에 대한 정확한 이해가 필요하다. 이번 문제를 통해 조금 더 다익스트라 알고리즘에 친해졌다.
다익스트라 알고리즘을 통해 최단거리를 구하는데, 최단거리를 나타내는 ans배열에는 (최단거리, 진입노드) 튜플 배열로 생성하여 정보를 담아낸다.
마지막에는 ans배열을 통해 [ 도착지점 -> 출발지점 ] 역순으로 dfs탐색을 통해 경로와 거리를 출력해 준다.
예제를 기준으로 설명하면 다음과 같다.
1
2
3
4
5
0 : 1
2 : 1
3 : 1
1 : 1
4 : 4
다익스트라 알고리즘의 특성상, 매 탐색마다 가중치가 가장 작은 노드부터 탐색하기에 목적 노드를 방문 완료되면, 해당 노드의 경로는 이미 최단 경로인 것을 확신할 수 있다. 문제에서는 항상 시작 지점과 도착 지점의 경로가 있음을 보장했고, 테스트 케이스에서는 시간 초과와 메모리 초과를 유도하는 입력 값이 주어지기에 도착 노드의 방문이 확인되면 곧바로 탐색을 중단하고 갱신된 ans 배열을 통해 dfs 탐색을 수행하면 된다.