문제
17472번: 다리 만들기 2
첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. 둘째 줄부터 N개의 줄에 지도의 정보가 주어진다. 각 줄은 M개의 수로 이루어져 있으며, 수는 0 또는 1이다. 0은 바다, 1은 땅을 의미한다.
www.acmicpc.net
구현 방법
문제가 복잡하여 단계별로 생각해보았습니다.
1. 섬 번호 새겨주기
섬끼리의 구별을 위해 dfs를 통해각 섬마다 번호를 새겨주었습니다.
2. 섬 사이의 다리 길이 구하기
섬에서 갈 수 있는 모든 경우를 구해주었습니다. 한 방향으로만 갈 수 있으므로 먼저 상하좌우 중 한 방향을 선택하였습니다. 선택한 방향으로 쭉 이동하여 다른 섬과 만나게 되면 다리 우선순위 큐에 추가해줍니다. 주의사항은 다리의 길이는 2이상이므로 해당 조건을 추가해주었습니다.
3. 다리 결합
union-find를 이용하여 최소 스패닝 트리를 구해주었습니다. 다리 길이를 기준으로 정렬하여 union-find로 결합 여부를 확인하고 결합되지 않았다면 결합시켜 주었습니다.
구현 코드
package BOJ.Gold;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.PriorityQueue;
import java.util.StringTokenizer;
public class BOJ17472_다리만들기2 {
static int R, C, len;
static int[] roots;
static int[][] map, deltas= {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
static PriorityQueue<Bridge> queue;
static class Bridge implements Comparable<Bridge>{
int from, to, weight;
public Bridge(int from, int to, int weight) {
super();
this.from = from;
this.to = to;
this.weight = weight;
}
@Override
public int compareTo(Bridge o) {
return Integer.compare(this.weight, o.weight);
}
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
R = Integer.parseInt(st.nextToken());
C = Integer.parseInt(st.nextToken());
map = new int[R][C];
queue = new PriorityQueue<>();
for (int r=0;r<R;r++) {
st = new StringTokenizer(br.readLine());
for (int c=0;c<C;c++) {
map[r][c] = Integer.parseInt(st.nextToken());
}
}
//입력완료
// 섬 번호 새겨주기
int island = 2;
for (int r=0;r<R;r++) {
for (int c=0;c<C;c++) {
if (map[r][c]==1) {
map[r][c] = island;
dfs(r, c, island);
island++;
}
}
}
// 섬 사이의 다리 길이 구하기
for (int r=0;r<R;r++) {
for (int c=0;c<C;c++) {
if (map[r][c]!=0) {
for (int d=0;d<4;d++) {
int nr = r+deltas[d][0];
int nc = c+deltas[d][1];
if (isIn(nr, nc) && map[nr][nc]==0) {
len = 1;
lengthCheck(nr, nc, d, map[r][c]);
}
}
}
}
}
// 다리 결합!!!
roots = new int[island-2];
makeSet();
int cnt = 0;
int ans = 0;
boolean flag = true;
while(!queue.isEmpty()) {
Bridge now = queue.poll();
if (union(now.from, now.to)) {
ans+=now.weight;
if (++cnt==roots.length-1) {
flag = false;
break;
}
}
}
if (flag) System.out.println(-1);
else System.out.println(ans);
}
static void makeSet() {
for (int i=0;i<roots.length;i++) {
roots[i] = -1;
}
}
static int findSet(int x) {
if (roots[x]<0) return x;
return roots[x] = findSet(roots[x]);
}
static boolean union(int x, int y) {
x = findSet(x);
y = findSet(y);
if (x==y) return false;
roots[y] = x;
return true;
}
static void lengthCheck(int r, int c, int d, int island) {
int dest = 0;
boolean flag = false;
while(true) {
int nr = r+deltas[d][0];
int nc = c+deltas[d][1];
if (isIn(nr, nc) && map[nr][nc]!=0) {
dest = map[nr][nc];
break;
} else if (!isIn(nr, nc)){
flag = true;
break;
}
r = nr; c = nc;
len++;
}
if (!flag && len!=1) queue.offer(new Bridge(island-2, dest-2, len));
}
static void dfs(int r, int c, int island) {
for (int d=0;d<4;d++) {
int nr = r+deltas[d][0];
int nc = c+deltas[d][1];
if (isIn(nr, nc) && map[nr][nc]==1) {
map[nr][nc] = island;
dfs(nr, nc, island);
}
}
return;
}
static boolean isIn(int nr, int nc) {
return nr>=0 && nr<R && nc>=0 && nc<C;
}
}
'백준' 카테고리의 다른 글
백준 16235번 : 나무 재테크 (0) | 2021.04.09 |
---|---|
백준 2146번 : 다리 만들기 (0) | 2021.04.09 |
백준 14502번 : 연구소 (0) | 2021.04.05 |
백준 1600번 : 말이 되고픈 원숭이 (0) | 2021.04.05 |
백준 9205번 : 맥주 마시면서 걸어가기 (0) | 2021.04.05 |