각 점에 가중치가 부여된 트리가 주어집니다. 당신은 다음 연산을 통하여, 이 트리의 모든 점들의 가중치를 0으로 만들고자 합니다.
트리의 각 점의 가중치를 의미하는 1차원 정수 배열 a와 트리의 간선 정보를 의미하는 edges가 매개변수로 주어집니다. 주어진 행동을 통해 트리의 모든 점들의 가중치를 0으로 만드는 것이 불가능하다면 -1을, 가능하다면 최소 몇 번만에 가능한지를 찾아 return 하도록 solution 함수를 완성해주세요. (만약 처음부터 트리의 모든 정점의 가중치가 0이라면, 0을 return 해야 합니다.)
a[i]는 i번 정점의 가중치를 의미합니다.[u, v] 2개의 정수로 이루어져 있으며, 이는 u번 정점과 v번 정점이 간선으로 연결되어 있음을 의미합니다.| a | edges | result |
|---|---|---|
| [-5,0,2,1,2] | [[0,1],[3,4],[2,3],[0,3]] | 9 |
| [0,1,0] | [[0,1],[1,2]] | -1 |
연결된 Node가 가장 적은 Node를 Root Node로 설정한다. (기각)
Root Node로부터 BFS를 사용하여 Leaf Node까지의 경로를 파악한다.
경로를 역순으로 탐색하며 (Bottom-up traverse) 자식 Node의 weight를 0으로 만들고, 이를 부모 Node에 더해준다.
Root Node의 weight가 0일 경우 연산횟수를 반환하며, 아닐 경우 -1(실패)을 반환한다.
[0,3,1,4,2]라고 가정하자.



def find_tree(a, edges):
# 1. 각 Node에 연결된 Node를 Dict로 표현
dic = {}
for edge in edges:
v1 = edge[0]
v2 = edge[1]
if v1 not in dic.keys():
dic[v1] = []
if v2 not in dic.keys():
dic[v2] = []
dic[v1].append(v2)
dic[v2].append(v1)
# 2. 연결된 Node가 가장 작은 Node를 Root Node로 설정.
root_index = min(dic.keys(), key = lambda x : len(dic[x]))
# 3. 설정된 Root를 기반으로 BFS 경로 파악
visited = [False] * len(a)
visited[root_index] = True
visited[dic[root_index][0]] = True
queue = [(root_index, dic[root_index].pop(0))]
path = []
while queue:
parent, child = queue.pop(0)
path.append((parent, child))
for child_ in dic[child]:
if visited[child_] is False:
queue.append((child, child_))
visited[child_] = True
return root_index, path[::-1] #경로를 역순으로 출력
def solution(a, edges):
answer = 0
root_index, path = find_tree(a, edges)
# 4. 자식 Node의 가중치를 부모 Node에 더해주면서 Bottom-up Traversal
for parent, child in path:
c_weight = a[child]
answer += abs(c_weight)
a[child] += -1 * c_weight
a[parent] += c_weight
if a[root_index] == 0:
return answer
else:
return -1
return answer
첫 번째 시도는 4개의 문제에서 시간초과가 발생했다.
edges를 dict로 만드는 데 문제가 있나 싶어 defaultdict를 사용해보았지만 오히려 더 오래 걸리는 경우가 있었다.구글링 결과, list.pop(0)의 시간복잡도가 O(N)이라는 것을 확인했고, list 대신에 queue를 사용하여 풀어보기로 하였다.
queue를 import하여 사용했을 때, list를 사용했을 때보다 압도적으로 오랜 시간이 걸렸고, 일반적으로 deque를 queue처럼 사용한다는 사실을 확인하였다.from collections import deque
def find_tree(a, edges):
dic = {}
for edge in edges:
v1 = edge[0]
v2 = edge[1]
if v1 not in dic.keys():
dic[v1] = []
if v2 not in dic.keys():
dic[v2] = []
dic[v1].append(v2)
dic[v2].append(v1)
# 바뀐 부분 : list 대신에 dequeue를 사용
q = deque([(-1,0)])
path = []
visited = [0] * len(a)
visited[0] = 1
while q:
p,c = q.popleft()
path.append((p,c))
des = dic[c]
if des:
for d in des:
if not visited[d]:
q.append((c,d))
visited[d] = True
return path[::-1]
def solution(a, edges):
answer = 0
path = find_tree(a, edges)
for parent, child in path[:-1]: #맨 마지막은 (-1,0)이므로
c_weight = a[child]
answer += abs(c_weight)
a[child] += -1 * c_weight
a[parent] += c_weight
if a[0] == 0:
return answer
else:
return -1
return answer
기존에는 stack, queue, deque를 구현하는 데 있어서 모두 list를 사용하였다. 그러나 element를 추가, 제거하는 데 있어서 list가 비효율적이라는 것을 확인하였고, 효율적인 자료구조형태를 사용하는 것이 좋다는 것을 깨달았다.