gimmickbutreal
[프로그래머스/자바] 전력망을 둘로 나누기 - bfs 본문
문제 설명
n개의 송전탑이 전선을 통해 하나의 트리 형태로 연결되어 있습니다. 당신은 이 전선들 중 하나를 끊어서 현재의 전력망 네트워크를 2개로 분할하려고 합니다. 이때, 두 전력망이 갖게 되는 송전탑의 개수를 최대한 비슷하게 맞추고자 합니다.
송전탑의 개수 n, 그리고 전선 정보 wires가 매개변수로 주어집니다. 전선들 중 하나를 끊어서 송전탑 개수가 가능한 비슷하도록 두 전력망으로 나누었을 때, 두 전력망이 가지고 있는 송전탑 개수의 차이(절대값)를 return 하도록 solution 함수를 완성해주세요.
제한사항
- n은 2 이상 100 이하인 자연수입니다.
- wires는 길이가 n-1인 정수형 2차원 배열입니다.
- wires의 각 원소는 [v1, v2] 2개의 자연수로 이루어져 있으며, 이는 전력망의 v1번 송전탑과 v2번 송전탑이 전선으로 연결되어 있다는 것을 의미합니다.
- 1 ≤ v1 < v2 ≤ n 입니다.
- 전력망 네트워크가 하나의 트리 형태가 아닌 경우는 입력으로 주어지지 않습니다.
입출력 예
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개의 송전탑을 가지게 되며, 이 방법이 최선입니다.
풀이 방법
그래프 문제를 dfs 알고리즘을 사용해서 풀이하는 방식이다.
1. 그래프 초기화
graph는 노드 개수 n에 따른 그래프의 인접 리스트를 저장하기 위한 배열이다.
min은 트리의 최소 크기
graph = new ArrayList[n + 1];
min = Integer.MAX_VALUE;
for (int i = 1; i <= n; i++) {
graph[i] = new ArrayList<>();
}
2. 양방향 간선 추가
양방향 간선으로 이루어진 그래프 자료구조이다. wirees 배열을 순회하면서 간선 정보를 graph 배열에 추가하고, add를 두 번 해준다.
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);
}
3. 입력된 간선을 하나씩 제거하면서 그래프를 두 개의 그룹으로 분리
visited 배열은 방문 여부를 나타내기 위한 배열로, 각 노드의 방문 상태를 저장한다.
전력망을 두 부분으로 나눌 때, 주어진 간선 중 하나를 끊어 두 부분으로 분리하기 위함이다.
dfs 함수를 재귀적으로 호출하여 시작점에서부터 각 그룹의 노드 개수를 계산
그룹의 노드 개수 차이를 구하고, 최소 트리 크기인 min을 갱신
제거한 단선을 다시 graph에 추가하여 다음 간선을 제거하고 계산을 진행
for (int i = 0; i < wires.length; i++) {
int v1 = wires[i][0];
int v2 = wires[i][1];
boolean[] visited = new boolean[n + 1];
graph[v1].remove(Integer.valueOf(v2));
graph[v2].remove(Integer.valueOf(v1));
int cnt = dfs(1, visited);
int diff = Math.abs(cnt - (n - cnt));
min = Math.min(min, diff);
graph[v1].add(v2);
graph[v2].add(v1);
}
4. dfs 함수
현재 노드 v를 방문처리하고, 노드 개수를 세는 변수인 cnt를 1로 초기화
graph[v]에 저장된 인접한 노드들을 순회하면서 방문하지 않은 노드들을 탐색
방문하지 않은 인접 노드 next를 재귀적으로 방문하며 cnt 업데이트
탐색이 끝나면 cnt 값 반환
static int dfs(int v, boolean[] visited) {
visited[v] = true;
int cnt = 1;
for (int next : graph[v]) {
if (!visited[next]) {
count += dfs(next, visited);
}
}
return cnt;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
import java.util.ArrayList;
class Solution {
static ArrayList<Integer>[] graph;
static int min;
public int solution(int n, int[][] wires) {
graph = new ArrayList[n + 1];
min = Integer.MAX_VALUE;
// 그래프 ArrayList 초기화. 노드 개수만큼 ArrayList 생성
for (int i = 1; i <= n; i++) {
graph[i] = new ArrayList<>();
}
// 양방향 간선 구조이므로 두 번 add를 해준다
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];
boolean[] visited = new boolean[n + 1];
// 해당 간선을 그래프에서 제거
graph[v1].remove(Integer.valueOf(v2));
graph[v2].remove(Integer.valueOf(v1));
int cnt = dfs(1, visited); // 임의의 시작점에서 dfs 탐색
int diff = Math.abs(cnt - (n - cnt));
min = Math.min(min, diff);
// 그래프에 다시 간선 추가
graph[v1].add(v2);
graph[v2].add(v1);
}
return min;
}
static int dfs(int v, boolean[] visited) {
visited[v] = true;
int cnt = 1;
for (int next : graph[v]) {
if (!visited[next]) {
cnt += dfs(next, visited);
}
}
return cnt;
}
}
|
cs |
'Algorithm > Java' 카테고리의 다른 글
[프로그래머스/자바] 두 개 뽑아서 더하기 - set (0) | 2023.06.11 |
---|---|
[프로그래머스/자바] K번째 수 - sort (0) | 2023.06.09 |
[프로그래머스/자바] 문자열 내 마음대로 정렬하기 - int (0) | 2023.06.08 |
[프로그래머스/자바] 숫자 문자열과 영단어 - HashMap (2) | 2023.06.07 |
[프로그래머스/자바] [1차] 비밀지도 - Binary (0) | 2023.06.06 |