[BOJ] 17472 다리 만들기 2 - JAVA
📚 Problem
📝 Solution
- N X M 크기의 지도에 섬이 여러개 있다
- 섬끼리 연결하는 다리는 직선으로 가로 또는 세로만 만들 수 있고 길이는 2 이상이어야 한다
- 모든 섬을 연결하는 다리 길이의 최솟값 구하기
Key Idea
- 지도에 있는 섬을 파악하기 위해 BFS를 사용해서 섬의 가장자리(값이 1이면서 상하좌우 중 하나라도 0) Point 들을 Island 객체 하나에 저장
- Island 객체들 간의 Point 를 비교하여 수직 수평에 있는 점들 사이의 거리를 구하여 2 이상인 것들을 추려내어
- Island 2개의 인덱스와 둘간의 거리를 저장한 Edge 객체를 생성하여 우선순위 큐에 삽입합니다
- 이후 union , find 를 이용하여 섬간 연결 거리의 최솟값을 구합니다
- 모든 섬을 연결하는 다리들이 최소거리 일 때, 이 다리들의 개수는
섬의 개수보다 하나 작은 값
이 됩니다- 따라서, 모든 섬을 연결하는 것이 불가능 한 경우는 다리의 개수가
섬의 개수 - 1
가 아닐 때가 됩니다
- map 을 반복문 돌면서 만약 1인 부분을 만나게 되면,
- 그 지점부터 BFS를 수행하여 해당 Point 가 섬의 가장자리인지를 판단합니다
- 가장자리는 map의 값이 1이면서 상하좌우중 0의 값이 한개라도 있는 Point 입니다
private static void BFS(int x, int y, ArrayList<Point> tempPoints){
Queue<Point> queue = new LinkedList<>();
Point point = new Point(x, y);
queue.add(point);
visited[point.x][point.y] = true;
while (!queue.isEmpty()){
Point current = queue.poll();
boolean check = false;
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 && !visited[nx][ny]){
if(map[nx][ny] == 1){
queue.add(new Point(nx, ny));
visited[nx][ny] = true;
}
else
check = true;
}
}
if(check)
tempPoints.add(current);
}
}
- 가장자리 Point 들로 이루어진 Island 객체들의 Point 들을 비교하여
- 만약 Point 끼리 서로 같은 행 또는 열에 있고,
- 거리가 2가 넘으면서 사이에 섬이 있는지를 검사합니다
- 만약 조건에 해당된다면 Island 객체의 각각의 인덱스와 다리의 길이를 우선순위 큐에 저장합니다
private static void bridge(){
for(int i = 0; i < islands.size() - 1; i++)
for (int j = i + 1; j < islands.size(); j++)
for(Point a : islands.get(i).points)
for(Point b : islands.get(j).points)
if(a.x == b.x || a.y == b.y){
int distance = Math.abs(a.x - b.x) + Math.abs(a.y - b. y) - 1;
if(distance >= 2 && !isIsland(a, b))
priorityQueue.add(new Edge(i, j, distance));
}
- 우선순위 큐에서 하나씩 Edge 를 꺼내서
- 만약 Island 객체간의 부모 노드가 다르다면
- 서로 union 해주며 결과값에 가중치를 더해줍니다
- 그리고 union 할때마다 cnt++ 해주어 만들어 지는 다리의 개수를 셉니다
- 만약 result 가
islands.size() - 1
가 아니라면 모든 섬을 연결할 수 없는 경우이므로 값을 -1로 바꿔줍니다
while(!priorityQueue.isEmpty()){
Edge current = priorityQueue.poll();
if(find(current.u) != find(current.v)){
union(current.u, current.v);
result += current.w;
cnt++;
}
}
if(cnt != islands.size() - 1)
result = -1;
💻 Code
import java.util.*;
class Point{
int x;
int y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
}
class Island{
ArrayList<Point> points;
public Island(ArrayList<Point> points){
this.points = points;
}
}
class Edge implements Comparable<Edge>{
int u;
int v;
int w;
public Edge(int u, int v, int w){
this.u = u;
this.v = v;
this.w = w;
}
@Override
public int compareTo(Edge o) {
return w - o.w;
}
}
public class Main {
private static int N, M, result;
private static int[] dx = {0, 1, 0, -1};
private static int[] dy = {1, 0, -1, 0};
private static int[] parent;
private static int[][] map;
private static boolean[][] visited;
private static ArrayList<Island> islands = new ArrayList<>();
private static PriorityQueue<Edge> priorityQueue = new PriorityQueue<>();
public static void main(String[] args) {
int cnt = 0;
Scanner scanner = new Scanner(System.in);
N = scanner.nextInt();
M = scanner.nextInt();
map = new int[N][M];
visited = new boolean[N][M];
for(int i = 0; i < N; i++)
for(int j = 0; j < M; j++)
map[i][j] = scanner.nextInt();
for(int i = 0; i < N; i++)
for(int j = 0; j < M; j++)
if(map[i][j] == 1 && !visited[i][j]){
ArrayList<Point> tempPoints = new ArrayList<>();
BFS(i, j, tempPoints);
islands.add(new Island(tempPoints));
}
parent = new int[islands.size()];
for(int i = 0; i < islands.size(); i++)
parent[i] = i;
bridge();
while(!priorityQueue.isEmpty()){
Edge current = priorityQueue.poll();
if(find(current.u) != find(current.v)){
union(current.u, current.v);
result += current.w;
cnt++;
}
}
if(cnt != islands.size() - 1)
result = -1;
System.out.println(result);
scanner.close();
}
private static void BFS(int x, int y, ArrayList<Point> tempPoints){
Queue<Point> queue = new LinkedList<>();
Point point = new Point(x, y);
queue.add(point);
visited[point.x][point.y] = true;
while (!queue.isEmpty()){
Point current = queue.poll();
boolean check = false;
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 && !visited[nx][ny]){
if(map[nx][ny] == 1){
queue.add(new Point(nx, ny));
visited[nx][ny] = true;
}
else
check = true;
}
}
if(check)
tempPoints.add(current);
}
}
private static boolean isIsland(Point a, Point b){
int nx = a.x - b.x, ny = a.y - b.y;
if(nx == 0){ // same row
if(ny < 0){
for(int i = a.y + 1; i < b.y; i++)
if(map[a.x][i] == 1)
return true;
}
else{
for(int i = b.y + 1; i < a.y; i++)
if(map[a.x][i] == 1)
return true;
}
}
else{ // same col
if(nx < 0){
for(int i = a.x + 1; i < b.x; i++)
if(map[i][a.y] == 1)
return true;
}
else{
for(int i = b.x + 1; i < a.x; i++)
if(map[i][a.y] == 1)
return true;
}
}
return false;
}
private static void bridge(){
for(int i = 0; i < islands.size() - 1; i++)
for (int j = i + 1; j < islands.size(); j++)
for(Point a : islands.get(i).points)
for(Point b : islands.get(j).points)
if(a.x == b.x || a.y == b.y){
int distance = Math.abs(a.x - b.x) + Math.abs(a.y - b. y) - 1;
if(distance >= 2 && !isIsland(a, b))
priorityQueue.add(new Edge(i, j, distance));
}
}
private static int find(int n){
if(parent[n] == n)
return n;
return parent[n] = find(parent[n]);
}
private static void union(int a, int b){
int parent_a = find(a), parent_b = find(b);
if(parent_a < parent_b)
parent[parent_b] = parent_a;
else
parent[parent_a] = parent_b;
}
}
Author And Source
이 문제에 관하여([BOJ] 17472 다리 만들기 2 - JAVA), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@choi-jaewon/BOJ-17472-다리-만들기-2-JAVA저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)