Skip to content

Commit

Permalink
new post
Browse files Browse the repository at this point in the history
  • Loading branch information
chae-jpg committed Dec 25, 2023
1 parent 92f7c79 commit beb7f95
Showing 1 changed file with 112 additions and 0 deletions.
112 changes: 112 additions & 0 deletions _posts/2023-12-25-BOJ 15681 트리와 쿼리.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
---
author: cakenpeace
categories: PS
tag: 트리, dp
subtitle: 트리 dp... 별 거 아니다!
---

안녕하세요

크리스마스 기념으로 트리 문제를 오랜만에 풀어보았습니다
역시 PS는 오랜만에 해도 재미있다... 쉬운 문제라 그런 거 같기는 한데ㅋ

---

사실 이 문제는 어떻게 풀어야 하는지 수도코드가 거의 다 문제에 주어져있어서 그것만 이해하면 코드 짜고 풀면 바로 땡이긴 함...
파이썬 기반인 게 C++ 쓰는 내 입장으로는 조금 귀찮긴 한데

```C++
#include <bits/stdc++.h>

using namespace std;

int n, r, q;
vector<vector<int>> graph;
vector<vector<int>> tree;
vector<bool> visited;
vector<int> cnt;

void makeTree(){
queue<int> qi;
qi.push(r);
visited[r] = true;
while (!qi.empty()) {
int top = qi.front();
qi.pop();

for (int a : graph[top]) {
if (visited[a]) continue;
visited[a] = true;
tree[top].push_back(a);
qi.push(a);
}
}

}

void calcChild(int a) {
for (int b : tree[a]) {
calcChild(b);
cnt[a] += cnt[b];
}
}

int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);

cin >> n >> r >> q;
graph.assign(n+1, vector<int> ());
tree.assign(n+1, vector<int> ());
visited.assign(n+1, false);
cnt.assign(n+1, 1);

for (int i = 0; i < n-1; i++) {
int a, b;
cin >> a >> b;
graph[a].push_back(b);
graph[b].push_back(a);
}
makeTree();
calcChild(r);

while (q--){
int a; cin >> a;
cout << cnt[a] << '\n';
}
}
```
일단 입력을 다 받아줌.
간선의 개수? 당연히 n-1개. 왜냐하면 트리니까.
근데 지금 내가 받은 입력은 부모 자식 관계가 정의되어 있는 트리가 아니라 그래프 형태임. 따라서 이걸 트리로 변환을 해줘야 함.
이 때 루트 노드가 처음에 주어졌으니까 그 노드를 시작으로 해서 쭉쭉 탐색을 해 나가면 누가 부모고 자식인지를 판단할 수 있을 것임.
이게 makeTree 함수의 기능임. bfs로 계속해서 그래프 탐색하고, 내 아래에 있으면 자식이라고 저장해둔 다음에 방문처리 하고, 큐에 넣고 무한 반복.
이렇게 트리를 만들었으면 이제 남은 건 자식 노드 개수 구하는 것 뿐임.
**근데**! 여기서 내가 놓친 부분이 있음.
```C++
int calcChild(int a) {
int cnt = 1;
for (int b : tree[a]) {
cnt += calcChild(b);
}
return cnt;
}
```
내가 처음에 짰던 자식 노드 개수 구하는 함수임.

타겟 노드를 인자로 받고 그 노드에 대해서 dfs로 계속 탐색하면서 결과적으로 개수를 리턴한다는 방식인데... **시간초과 엔딩**

이유야 뭐... 진짜 운이 나빠서 모든 부모 노드의 자식 개수가 1개이고 (즉 트리가 1자) 쿼리에서 요구하는 노드는 가장 마지막 자식 노드, 그리고 쿼리 개수는 1e5개라고 가정하자.

이러면 1e5번 탐색을 1e5번 하니까 1e10번 계산을 해야하는 것이 되고... 그러면 100억이니까 당연히? 시간초과겠죠?

따라서 여기서는 메모이제이션으로 문제를 풀어줘야함... 이때 사용하는 성질이 문제의 그 구구절절한 힌트에도 나와있지만 자식 정점에 대한 함수가 호출되면, 그 자식을 루트로 하는 트리가 구성된다는 것.
그래서 자식 노드로 함수 호출한 뒤에 내 노드에 자식 노드의 자식 개수 크기를 더해주는 식으로 재귀로 함수를 짜면 이제 차곡차곡 수가 쌓여서 결과적으로는 모든 정점에 대한 자식 노드의 개수를 구할 수 있을 것임~ 간단하죠?

그런 문제였다... 트리 dp 처음 해보는데 이런 거구나 감 좀 잡힌듯?
dp 연습 진짜 많이 해야겠다고 새삼스럽게 또 느꼈고... 이건 탑다운 dp인데 바텀업... 구현하려면 애초에 트리를 구성하는 방식 자체가 변해야 할 것 같고...
아무튼 오랜만에 재미있었던 것 같음~

0 comments on commit beb7f95

Please sign in to comment.