https://www.acmicpc.net/problem/11725
방향을 알 수 있는 정보가 루트가 1이라는거 밬에 없기 때문에 처음에 어떻게 트리를 구현해야할지 막막했다.
우선 트리의 값들을 양방향 그래프라고 생각하고 전부 받아 온 뒤 1부터 연결되어 있는 노드를 들여다 보며 차수를 계산한 뒤 배열에 저장해두고 나중에 그래프에서 꺼낸 뒤 연결된 노드간의 차수가 가장 작은걸 출력하는 방식으로 풀어보았다.
N < 100,000 이고 시간복잡도는 O(N)이 되므로 풀이가 가능할꺼같다.
import java.io.*;
import java.util.*;
public class Main {
public static int N;
public static int[] degree;
public static List<Integer>[] list;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
StringBuilder sb = new StringBuilder();
// 1. 입력받기
N = Integer.parseInt(br.readLine());
degree = new int[N + 1];
list = new ArrayList[N + 1];
for(int i = 1; i <= N; i++){
list[i] = new ArrayList<>();
}
// 1-1. 양방향 그래프 입력
for(int i = 0; i < N - 1; i++){
st = new StringTokenizer(br.readLine());
int u = Integer.parseInt(st.nextToken());
int v = Integer.parseInt(st.nextToken());
list[u].add(v);
list[v].add(u);
}
// 2. 차수 계산
Queue<Integer> q = new LinkedList<>();
int cnt = 2;
q.offer(1);
degree[1] = 1;
while(!q.isEmpty()){
int size = q.size();
for(int i = 0; i < size; i++){
int cur = q.poll();
for(int child : list[cur]) {
if(degree[child] == 0) {
degree[child] = cnt;
q.offer(child);
}
}
}
cnt++;
}
// 3. 부모노드 탐색
for(int i = 2; i <= N; i++){
int min = Integer.MAX_VALUE;
int parent = 0;
for(int j = 0; j < list[i].size(); j++){
int x = list[i].get(j);
if(degree[x] < min){
min = degree[x];
parent = x;
}
}
sb.append(parent).append("\n");
}
System.out.println(sb);
}
}
조금 더 깔끔하게 풀 수 있을꺼같은데 어렵다.
GPT 피드백 중 참고할 만한 사항들
1. 용어 혼동: 변수명이 degree인데 실제로는 “차수(인접 노드 수)”가 아니라 “깊이(depth)”입니다. 주석도 “차수 계산”이 아니라 “깊이 계산”이 맞아요. 나중에 본인이 봐도 헷갈릴 수 있으니 depth로 바꾸는 게 좋아요.
이건 정말 나중에 면접을 볼때나 의사소통 할때 큰일 날뻔했다. 차수가 아니라 깊이
2. 2단계 로직 단순화 가능: 지금은 (1) BFS로 깊이 계산 → (2) 모든 노드의 이웃을 돌며 가장 얕은 쪽을 부모로 선택, 이렇게 두 번 돕니다. BFS 중에 바로 parent[child] = cur; 로 기록하면 2단계를 없앨 수 있어요. 시간·코드 모두 더 깔끔해집니다.
먼저 깊이를 다 구하고 비교하려고 했는데 생각해보니 BFS중에 충분히 기록할 수 있을꺼같다.
3. 큐 구현: LinkedList보다 ArrayDeque가 큐 용도로는 더 빠르고 메모리 효율적입니다.
습관적으로 큐를 구현할때 LinkedList를 사용했는데 이건 좀 생각해 볼 여지가 있다.
4. 방문 체크: 현재는 degree[child] == 0을 방문 체크로 쓰고 있는데, parent 배열을 방문 마킹으로 겸용하면 명확해집니다(parent[nxt] == 0이면 미방문).
부모를 깊이를 계산하며 바로 채워주므로 한번에 계산할꺼면 이 방법을 쓰는게 확실히 좋아보인다.
5. 재귀 DFS 지양: 자바에서 재귀 DFS는 100k 노드에선 스택오버플로우 위험이 있어요. 현재처럼 반복형 BFS가 안전합니다.
재귀에서 스택오버플로우 걱정을 크게 해본적이 없었는데 반드시 참고할만한 사항인것같다.
다시 푼 풀이
import java.io.*;
import java.util.*;
public class Main {
public static int N;
public static int[] parent;
public static List<Integer>[] list;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
StringBuilder sb = new StringBuilder();
// 1. 입력받기
N = Integer.parseInt(br.readLine());
parent = new int[N + 1];
list = new ArrayList[N + 1];
for(int i = 1; i <= N; i++){
list[i] = new ArrayList<>();
}
// 1-1. 양방향 그래프 입력
for(int i = 0; i < N - 1; i++){
st = new StringTokenizer(br.readLine());
int u = Integer.parseInt(st.nextToken());
int v = Integer.parseInt(st.nextToken());
list[u].add(v);
list[v].add(u);
}
// 2. 차수 계산
Queue<Integer> q = new LinkedList<>();
q.offer(1);
parent[1] = 1;
while(!q.isEmpty()){
int cur = q.poll();
for(int child : list[cur]) {
if(parent[child] == 0) {
parent[child] = cur;
q.offer(child);
}
}
}
for(int i = 2; i <= N; i++){
sb.append(parent[i]).append("\n");
}
System.out.println(sb);
}
}
BFS라는 풀이방법은 같지만 생각의 전환에 따라 훨씬 간단하게 풀 수 있는 문제였다.
풀이 방법이 생각났다고 바로 손대지말고 더 편하고 빠르게 풀 수 있는 방법이 있는지 조금 생가해보는 시간을 가지는것도 좋은듯하다.
'코딩테스트 > JAVA' 카테고리의 다른 글
| [백준 JAVA] 14503번: 로봇 청소기 (0) | 2025.10.01 |
|---|---|
| [백준 JAVA] 12865번: 평범한 배낭 (0) | 2025.10.01 |
| [백준 JAVA] 11660번: 구간 합 구하기 5 (0) | 2025.10.01 |
| [백준 JAVA] 11053번: 가장 긴 증가하는 부분 수열 (0) | 2025.09.30 |
| [백준 JAVA] 9663번: N - Queen (0) | 2025.09.25 |