문제
시간 제한 메모리 제한 제출 정답 맞힌 사람 정답 비율
1 초 | 512 MB | 22928 | 8320 | 5196 | 32.708% |
문제
섬으로 이루어진 나라가 있고, 모든 섬을 다리로 연결하려고 한다. 이 나라의 지도는 N×M 크기의 이차원 격자로 나타낼 수 있고, 격자의 각 칸은 땅이거나 바다이다.
섬은 연결된 땅이 상하좌우로 붙어있는 덩어리를 말하고, 아래 그림은 네 개의 섬으로 이루어진 나라이다. 색칠되어있는 칸은 땅이다.
다리는 바다에만 건설할 수 있고, 다리의 길이는 다리가 격자에서 차지하는 칸의 수이다. 다리를 연결해서 모든 섬을 연결하려고 한다. 섬 A에서 다리를 통해 섬 B로 갈 수 있을 때, 섬 A와 B를 연결되었다고 한다. 다리의 양 끝은 섬과 인접한 바다 위에 있어야 하고, 한 다리의 방향이 중간에 바뀌면 안된다. 또, 다리의 길이는 2 이상이어야 한다.
다리의 방향이 중간에 바뀌면 안되기 때문에, 다리의 방향은 가로 또는 세로가 될 수 밖에 없다. 방향이 가로인 다리는 다리의 양 끝이 가로 방향으로 섬과 인접해야 하고, 방향이 세로인 다리는 다리의 양 끝이 세로 방향으로 섬과 인접해야 한다.
섬 A와 B를 연결하는 다리가 중간에 섬 C와 인접한 바다를 지나가는 경우에 섬 C는 A, B와 연결되어있는 것이 아니다.
아래 그림은 섬을 모두 연결하는 올바른 2가지 방법이고, 다리는 회색으로 색칠되어 있다. 섬은 정수, 다리는 알파벳 대문자로 구분했다.
다리가 교차하는 경우가 있을 수도 있다. 교차하는 다리의 길이를 계산할 때는 각 칸이 각 다리의 길이에 모두 포함되어야 한다. 아래는 다리가 교차하는 경우와 기타 다른 경우에 대한 2가지 예시이다.
나라의 정보가 주어졌을 때, 모든 섬을 연결하는 다리 길이의 최솟값을 구해보자.
입력
첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. 둘째 줄부터 N개의 줄에 지도의 정보가 주어진다. 각 줄은 M개의 수로 이루어져 있으며, 수는 0 또는 1이다. 0은 바다, 1은 땅을 의미한다.
출력
모든 섬을 연결하는 다리 길이의 최솟값을 출력한다. 모든 섬을 연결하는 것이 불가능하면 -1을 출력한다.
제한
- 1 ≤ N, M ≤ 10
- 3 ≤ N×M ≤ 100
- 2 ≤ 섬의 개수 ≤ 6
풀이
문제요약
- 모든 섬을 연결하는 다리 길이의 최솟값을 구해야 함
- 다리의 방향은 변경될 수 없고, 길이는 2개 이상이어야 한다.
- 상하좌우로 붙어있다면 같은 섬이다.
- 섬의 경우 1, 바다의 경우 0으로 입력이 주어진다.
접근 아이디어
- 각 섬들별로 식별가능한 인덱스 부여 (BFS 탐색)
- 다리를 놓을 수 있는 모든 경우 탐색 (완전탐색)
- 연결 가능한 간선의 최소값 구하기 (최소 스패닝 트리 - 크루스칼)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
static int[] dx = new int[] {-1,1,0,0};
static int[] dy = new int[] {0,0,-1,1};
static ArrayList<int[]> bridges = new ArrayList<>();
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int answer = 0;
String[] input = br.readLine().split(" ");
int N = Integer.parseInt(input[0]);
int M = Integer.parseInt(input[1]);
int[][] graph = new int[N][M];
boolean[][] visited = new boolean[N][M];
for (int i = 0; i < N; i++) {
String[] inputs = br.readLine().split(" ");
for (int j = 0; j < M; j++) {
graph[i][j] = Integer.parseInt(inputs[j]);
}
}
int islandIndex = 1;
ArrayList<ArrayList<int[]>> lists = new ArrayList<>();
for (int i = 0; i <= 6; i++) { // 섬의 개수가 6개 이하이므로 6개만 만들면 댐
lists.add(new ArrayList<int[]>());
}
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (!visited[i][j] && graph[i][j] == 1) {
Queue<int[]> queue = new LinkedList<>();
queue.add(new int[] {i,j});
visited[i][j] = true;
graph[i][j] = islandIndex;
lists.get(islandIndex).add(new int[]{i,j});
while (!queue.isEmpty()) {
int[] value = queue.poll();
for (int k = 0; k < dx.length; k++) {
int cy = value[0] + dy[k];
int cx = value[1] + dx[k];
if (cx < 0 || cx >= M || cy < 0 || cy >= N) continue;
if (visited[cy][cx] || graph[cy][cx] == 0) continue;
int[] newVal = new int[]{cy,cx};
queue.add(newVal);
graph[cy][cx] = islandIndex;
lists.get(islandIndex).add(newVal);
visited[cy][cx] = true;
}
}
islandIndex++;
}
}
}
// // bridge에 현재값, 타겟값, 거리
for (int i = 0; i < lists.size(); i++) {
ArrayList<int[]> islands = lists.get(i);
for (int j = 0; j < islands.size(); j++) {
int[] island = islands.get(j);
for (int k = island[0]-1; k > 0; k--) { // 위쪽 탐색
if (graph[k][island[1]] == graph[island[0]][island[1]])
break;
if (graph[k][island[1]] != 0) { // 0 이 아니고, 현재 값과 다르다면
if (island[0] - k == 2)
break;
int[] bridge = new int[3];
bridge[0] = graph[island[0]][island[1]];
bridge[1] = graph[k][island[1]];
bridge[2] = island[0] - k - 1;
bridges.add(bridge);
break;
}
}
for (int k = island[0]+1; k < N; k++) { // 아래쪽 탐색
if (graph[k][island[1]] == graph[island[0]][island[1]])
break;
if (graph[k][island[1]] != 0) { // 0 이 아니고, 현재 값과 다르다면
if (k - island[0] == 2)
break;
int[] bridge = new int[3];
bridge[0] = graph[island[0]][island[1]];
bridge[1] = graph[k][island[1]];
bridge[2] = k - island[0] - 1;
bridges.add(bridge);
break;
}
}
for (int k = island[1]-1; k > 0; k--) { // 왼쪽 탐색
if (graph[island[0]][k] == graph[island[0]][island[1]])
break;
if (graph[island[0]][k] != 0) { // 0 이 아니고, 현재 값과 다르다면
if (island[1] - k == 2)
break;
int[] bridge = new int[3];
bridge[0] = graph[island[0]][island[1]];
bridge[1] = graph[island[0]][k];
bridge[2] = island[1] - k - 1;
bridges.add(bridge);
break;
}
}
for (int k = island[1]+1; k < M; k++) { // 오른쪽 탐색
if (graph[island[0]][k] == graph[island[0]][island[1]])
break;
if (graph[island[0]][k] != 0) { // 0 이 아니고, 현재 값과 다르다면
if (k - island[1] == 2)
break;
int[] bridge = new int[3];
bridge[0] = graph[island[0]][island[1]];
bridge[1] = graph[island[0]][k];
bridge[2] = k - island[1] - 1;
bridges.add(bridge);
break;
}
}
}
}
// 간선의 값을 기준으로 연결 시키기
Collections.sort(bridges, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[2] - o2[2];
}
});
int[] arr = new int[islandIndex];
for (int i = 1; i < arr.length; i++) {
arr[i] = i;
}
for (int i = 0; i < bridges.size(); i++) {
int[] bridge = bridges.get(i);
int a = find(arr, bridge[0]);
int b = find(arr, bridge[1]);
if (a == b) continue;
union(arr, a, b);
answer += bridge[2];
}
int parent = find(arr, arr[1]);
for (int i = 1; i < arr.length; i++) {
if (find(arr, i) != parent) {
answer = -1;
break;
}
}
if (answer == 0)
answer = -1;
System.out.println(answer);
}
public static int find(int[] array, int x) {
// 내 루트노드 찾기
if(array[x] == x) return x;
return array[x] = find(array, array[x]);
}
public static void union(int[] array, int x, int y) {
int a = find(array, x);
int b = find(array, y);
if(a==b)return;
array[b] = a;
}
}
'알고리즘' 카테고리의 다른 글
[백준] 20055번 컨베이어 벨트 위의 로봇 자바 풀이 (0) | 2023.09.14 |
---|---|
[백준] 1706번 크로스워드 자바 풀이 (0) | 2023.09.10 |
[백준] 2568번 전깃줄 - 2 자바 풀이 (0) | 2023.08.23 |
[백준] 3109번 빵집 자바 풀이 (0) | 2023.08.17 |
[백준] 16234번 인구 이동 자바 풀이 (0) | 2023.08.17 |