n개의 송전탑이 전선을 통해 하나의 트리 형태로 연결되어 있습니다. 당신은 이 전선들 중 하나를 끊어서 현재의 전력망 네트워크를 2개로 분할하려고 합니다. 이때, 두 전력망이 갖게 되는 송전탑의 개수를 최대한 비슷하게 맞추고자 합니다.
송전탑의 개수 n, 그리고 전선 정보 wires가 매개변수로 주어집니다. 전선들 중 하나를 끊어서 송전탑 개수가 가능한 비슷하도록 두 전력망으로 나누었을 때, 두 전력망이 가지고 있는 송전탑 개수의 차이(절대값)를 return 하도록 solution 함수를 완성해주세요.
제한사항
입출력 예
| n | wires | result |
|---|---|---|
| 9 | [[1,3],[2,3],[3,4],[4,5],[4,6],[4,7],[7,8],[7,9]] | 3 |
| 4 | [[1,2],[2,3],[3,4]] | 0 |
| 7 | [[1,2],[2,7],[3,7],[3,4],[4,5],[6,7]] | 1 |
입출력 예 설명
입출력 예 #1
다음 그림은 주어진 입력을 해결하는 방법 중 하나를 나타낸 것입니다.

4번과 7번을 연결하는 전선을 끊으면 두 전력망은 각 6개와 3개의 송전탑을 가지며, 이보다 더 비슷한 개수로 전력망을 나눌 수 없습니다.
또 다른 방법으로는 3번과 4번을 연결하는 전선을 끊어도 최선의 정답을 도출할 수 있습니다.
입출력 예 #2
다음 그림은 주어진 입력을 해결하는 방법을 나타낸 것입니다.

2번과 3번을 연결하는 전선을 끊으면 두 전력망이 모두 2개의 송전탑을 가지게 되며, 이 방법이 최선입니다.
입출력 예 #3
다음 그림은 주어진 입력을 해결하는 방법을 나타낸 것입니다.

3번과 7번을 연결하는 전선을 끊으면 두 전력망이 각각 4개와 3개의 송전탑을 가지게 되며, 이 방법이 최선입니다.
import java.util.*;
class Solution {
// 그래프의 연결을 저장할 배열
static ArrayList<Integer>[] graph;
static int answer;
// dfs 탐색 메서드
public int dfs(int v, boolean[] visit) {
// 입력받은 노드를 true
visit[v] = true;
// 해당 부분부터 count 시작
int count = 1;
// 입력받은 노드와 연결된 노드들 탐색
for(int next : graph[v]) {
// 방문한 적 없다면
if(!visit[next]) {
// 방문을 하고 return 값 저장
count += dfs(next, visit);
}
}
// 최종 탐색 개수 반환
return count;
}
public int solution(int n, int[][] wires) {
// graph와 answer 초기화
graph = new ArrayList[n + 1];
answer = Integer.MAX_VALUE;
// graph마다 ArrayList 생성
for(int i = 0; i <= n; i++) {
graph[i] = new ArrayList<>();
}
// 주어진 tree를 확인하면서 연결해줌
// 양방향 노드이기 때문에 둘 다 연결
for(int i = 0; i < wires.length; i++) {
int v1 = wires[i][0];
int v2 = wires[i][1];
graph[v1].add(v2);
graph[v2].add(v1);
}
for(int i = 0; i < wires.length; i++) {
// 노드를 입력받고
int v1 = wires[i][0];
int v2 = wires[i][1];
// visit 배열을 초기화해준 뒤
boolean[] visit = new boolean[n+1];
// 해당 노드의 연결을 끊어줌
graph[v1].remove(Integer.valueOf(v2));
graph[v2].remove(Integer.valueOf(v1));
// 이때 dfs 탐색을 통해 count를 찾고
int count = dfs(1, visit);
// 절댓값을 구해서
int diff = Math.abs(count - (n - count));
// answer에 있는 값과 비교하여 최솟값을 저장
answer = Math.min(answer, diff);
// 다시 연결해줌
graph[v1].add(v2);
graph[v2].add(v1);
}
return answer;
}
}
DFS 탐색을 사용해서 해결하는 문제였다.
먼저 dfs 탐색 메서드를 살펴보면, dfs 탐색 메서드에서는 노드와 visit 배열을 매개변수로 넘겨준다. visit 배열에서 해당 노드를 인덱스로 가진 값을 true로 바꿔주고 해당 부분부터 count를 시작한다.
입력받은 노드와 연결된 노드를 탐색해서 방문한 적 없다면 다시 dfs 탐색을 진행한다.
그렇게 되면 위의 과정을 동일하게 진행하게 될 것이고, 탐색이 끝난 뒤에 반환을 해주다보면 count에는 맨 처음 노드와 연결된 노드의 개수가 저장이 된다.
graph를 저장할 배열과 정답으로 반환이 되는 answer의 값을 초기화 시켜준다. 이때 graph는 ArrayList 배열이므로 배열의 크기만큼 ArrayList를 생성해주어야한다.
이후 wires 배열에 저장된 값을 통해 값을 연결시켜준다. 연결시켜주는 방식은 int v1, v2를 사용해서 v1을 인덱스로 하는 ArrayList에 v2를 삽입하고, v2를 인덱스로 하는 ArrayList에 v1을 삽입해주는 것이다. 양방향 노드이기 때문에 한쪽만 연결을 진행하는 것이 아닌 양쪽 모두 연결을 해주어야한다.
wires 배열의 길이만큼 반복을 진행한다. 배열에 있는 연결된 노드들을 각각 v1, v2에 저장을 해주고 visit 배열을 초기화 시켜준다. 그 뒤에 해당 노드의 연결을 끊어준다. 끊어진 graph를 가지고 dfs 탐색을 진행한다. 이때 임의의 값을 넣어서 진행을 하는데, 임의의 값을 넣어도 되는 이유는 깊이 탐색이기 때문에 임의의 값을 넣어도 그 값과 연결된 모든 노드를 탐색할 수 있기 때문이다. 이렇게 dfs 탐색을 통해 임의의 노드와 연결된 노드의 개수를 찾고 하나의 그래프와 다른 그래프의 차이의 절댓값을 구해준다.
예를 들어, n = 7이고 dfs 탐색을 통해 count = 2 라는 값이 나왔다고 하면, 끊어진 트리를 각각 비교해봤을 때 2, 5개로 나온다. 이때 두 노드의 개수의 차는 -3이고 조건에서 절댓값을 비교하기로 했으므로 절댓값을 씌워주면 3이 된다.
절댓값을 구한 뒤에 answer와 Math.min 함수를 통해 더 작은 값을 answer에 저장해가면서 비교를 진행한다.
그리고 다음 탐색을 위해서 그래프를 다시 연결해준다. 이 반복을 진행하면서 나오는 가장 작은 answer 값을 찾아 반환을 해주면 문제를 해결할 수 있다!
dfs와 그래프가 같이 사용된 문제였다. 그래프는 이전에 백준을 풀때도 많이 어려워했던 부분이라서 이번 문제를 순수 내 힘으로 풀진 못했고 다른 사람들의 풀이를 참고하고 공부해가면서 문제를 해결할 수 있었다. 그래도 문제를 풀면서 그래프 관련해서 공부를 좀 할 수 있었다. 다음에 이런 그래프 문제를 풀게 된다면 그땐 혼자의 힘으로 풀어보고 싶다..!