문제풀이/백준
[백준]17472: 다리 만들기 2 - JAVA
빈둥벤둥
2021. 8. 6. 15:11
[백준]17472: 다리 만들기 2
17472번: 다리 만들기 2
첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. 둘째 줄부터 N개의 줄에 지도의 정보가 주어진다. 각 줄은 M개의 수로 이루어져 있으며, 수는 0 또는 1이다. 0은 바다, 1은 땅을 의미한다.
www.acmicpc.net
풀이
🪑 구현 + BFS + DFS + MST + 완탐의 아이디어가 들어간 문제였다. 이 중에서 가장 중요한 부분은 MST이다.
📝 문제의 조건을 정리해 보자.
- 다리의 방향이 중간에 바뀔 수 없으며 길이는 2 이상이어야 한다.
- 섬을 모두 연결하는 다리의 최소 길이를 구한다.
- 다리는 교차될 수 있으며 교차되는 경우 각 칸이 각 다리의 길이에 모두 포함되어야 한다.
- 모든 섬을 연결하는 것이 불가능 하면 -1을 출력한다.
🔧 문제를 풀어보자!
- 각 섬에 고유 번호를 부여한다.
- 각 섬을 연결할 수 있는 모든 방법을 구한다.
- MST 알고리즘으로 최소 간선을 선택한다.
🔹 각 섬에 고유 번호를 부여한다.
- 완전 탐색을 하며 1인 노드를 찾고, 그 노드에서 BFS탐색으로 하며 같은 섬에 해당되는 노드들을 탐색한다.
- 이때 각 섬의 좌표를 리스트에 저장해 주었다.
🔹 섬을 연결할 수 있는 모든 방법을 구한다.
- DFS로 한 방향으로 갈 수 있는지 확인해 주었다.
- 탐색 중에 다른 num을 가진 섬을 만나고, 그때 건설할 수 있는 다리 길이가 2 이상이라면 우선순위 큐에 담아주었다.
- 우선순위 큐는 다리 길이 순서대로 내림차순 정렬한다.
🔹 MST 알고리즘으로 최소 간선을 선택한다.
- 앞서 구한 우선순위 큐를 사용하여 kruskal 알고리즘을 실행한다.
- MST를 구한 다음에 예외 처리를 진해한다.
- 현재 다리 길이의 합이 0이거나, 선택한 간선의 수가 전체 섬의 개수 -1이 아닌 경우는 모든 섬을 연결하는 것이 불가능 하다는 의미이므로 -1을 return하도록 했다.
코드
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
|
import java.io.*;
import java.util.*;
public class Main {
static int n, m;
static int[] dx = {0, 1, 0, -1};
static int[] dy = {-1, 0, 1, 0};
static int[][] board;
static boolean[][] visited;
static ArrayList<Node>[] list;
static PriorityQueue<Mst_Node> pq;
static int[] parent;
public static void main(String[] args) throws IOException {
//입력
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
String str = bf.readLine();
StringTokenizer st = new StringTokenizer(str);
n = Integer.parseInt(st.nextToken());
m = Integer.parseInt(st.nextToken());
board = new int[n][m];
for(int i = 0; i < n; i++) {
str = bf.readLine();
st = new StringTokenizer(str);
for(int j = 0; j < m; j++) {
board[i][j] = Integer.parseInt(st.nextToken());
}
}
//입력 끝
//섬마다 고유 숫자를 부여 - 완탐 bfs
list = new ArrayList[7]; //섬 개수 최대 6개
visited = new boolean[n][m];
int num = 1;
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
if(!visited[i][j] && board[i][j] == 1) {
list[num] = new ArrayList<>();
bfs(i, j, num);
num++;
}
}
}
//각 섬을 연결할 수 있는 방법을 모두 구한다.
pq = new PriorityQueue<>();
for(int i = 1; i < num; i++) {
for(int j = 0; j < list[i].size(); j++) {
Node island = list[i].get(j);
for(int k = 0; k < 4; k++) {
find_bridge(island.x, island.y, i, k, -1);
}
}
}
//mst알고리즘으로 최소 간선을 선택한다.
System.out.println(kruskal(num));
}
public static int kruskal(int num) {
parent = new int[num];
for(int i = 1; i < num; i++) {
parent[i] = i;
}
boolean[] link = new boolean[num];
int result = 0;
int bridge = 0;
while(!pq.isEmpty()) {
Mst_Node current = pq.poll();
int p1 = find(current.n1);
int p2 = find(current.n2);
if(p1 != p2) {
union(p1, p2);
link[current.n1] = true;
link[current.n2] = true;
result += current.length;
bridge++;
}
}
if(result == 0) return -1;
if(bridge != num - 2) return -1;
return result;
}
public static int find(int node) {
if(parent[node] == node) return node;
else return parent[node] = find(parent[node]);
}
public static void union(int a, int b) {
parent[a] = b;
}
public static void find_bridge(int x, int y, int num, int dir, int len) {
if(board[x][y] != 0 && board[x][y] != num) { //다른 섬을 만남
if(len >= 2) pq.offer(new Mst_Node(num, board[x][y], len));
return;
}
int nx = x + dx[dir];
int ny = y + dy[dir];
if(nx < 0 || ny < 0 || nx >= n || ny >= m) return;
if(board[nx][ny] == num) return;
find_bridge(nx, ny, num, dir, len + 1);
}
public static void bfs(int x, int y, int num) {
Queue<Node> q = new LinkedList<>();
q.offer(new Node(x, y));
visited[x][y] = true;
while(!q.isEmpty()) {
Node current = q.poll();
board[current.x][current.y] = num;
list[num].add(new Node(current.x, current.y));
for(int i = 0; i < 4; i++) {
int nx = current.x + dx[i];
int ny = current.y + dy[i];
if(nx < 0 || ny < 0 || nx >= n || ny >= m) continue;
if(visited[nx][ny] || board[nx][ny] != 1) continue;
visited[nx][ny] = true;
q.offer(new Node(nx, ny));
}
}
}
public static class Mst_Node implements Comparable<Mst_Node> {
int n1, n2, length;
public Mst_Node(int n1, int n2, int length) {
this.n1 = n1;
this.n2 = n2;
this.length = length;
}
@Override
public int compareTo(Mst_Node mst_n) {
return this.length - mst_n.length;
}
}
public static class Node {
int x, y;
public Node(int x, int y) {
this.x = x;
this.y = y;
}
}
}
|
cs |