아이디어
1. 섬 번호 붙이기
- 맵 전체를 탐색하며 섬을 발견하면 사방 탐색으로 인접한 모든 섬 타일에 번호를 붙인다.
- 이 섬 번호는 그래프에서 정점 번호로 사용한다.
2. 다리 연결 그래프 구성
- 맵 전체를 탐색하며 섬 타일이라면 4방향으로 다리를 뻗어본다.
- 다리가 같은 섬에 이어진다면 취소한다.
- 다리의 길이가 2보다 작다면 취소한다.
- 기존에 있던 다리보다 더 길다면 취소한다.
- 위 과정으로 섬을 정점, 다리를 간선으로 하는 가중치 그래프를 얻는다.
3. 최소 스패닝 트리
- 해당 그래프에 대해 최소 스패닝 트리의 총 길이를 구한다.
- 여기선 간단하게 O(V2)인 Prim 알고리즘을 사용했다.
코드
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
public class Main {
static BufferedReader rd = new BufferedReader(new InputStreamReader(System.in));
static StringTokenizer tk = null;
static int N, M, V, E, ans;
static int[][] map;
static int[][] graph;
static int[] dist;
static boolean[] visited;
static int[] dy = {-1, 0, 1, 0};
static int[] dx = {0, 1, 0, -1};
static int INF = Integer.MAX_VALUE / 2;
public static void main(String[] args) throws Exception {
tk = new StringTokenizer(rd.readLine());
N = Integer.parseInt(tk.nextToken());
M = Integer.parseInt(tk.nextToken());
map = new int[N][M];
for (int y=0; y<N; y++) {
tk = new StringTokenizer(rd.readLine());
for (int x=0; x<M; x++) {
map[y][x] = tk.nextToken().charAt(0) == '1' ? -1 : 0;
}
}
for (int y=0; y<N; y++) {
for (int x=0; x<M; x++) {
if (map[y][x] == -1) {
setIsland(y, x, ++V);
}
}
}
graph = new int[V+1][V+1];
for (int i=1; i<=V; i++) {
for (int j=1; j<=V; j++) {
graph[i][j] = (i == j) ? 0 : INF;
}
}
for (int y=0; y<N; y++) {
for (int x=0; x<M; x++) {
int src = map[y][x];
if (src <= 0) continue;
for (int d=0; d<4; d++) {
int[] result = measure(y, x, d);
if (result == null) continue;
int dest = result[0];
int len = result[1];
if (src == dest) continue;
if (len < 2) continue;
if (graph[src][dest] <= len) continue;
if (graph[src][dest] == INF) E++;
graph[src][dest] = graph[dest][src] = len;
}
}
}
dist = new int[V+1];
Arrays.fill(dist, INF);
dist[1] = 0;
visited = new boolean[V+1];
for (int i=1; i<=V; i++) {
int minIdx = -1;
int minDist = INF;
for (int j=1; j<=V; j++) {
if (!visited[j] && dist[j] < minDist) {
minDist = dist[j];
minIdx = j;
}
}
if (minIdx == -1) {
System.out.println(-1);
return;
}
visited[minIdx] = true;
ans += minDist;
for (int j=1; j<=V; j++) {
dist[j] = Math.min(dist[j], graph[minIdx][j]);
}
}
System.out.println(ans);
return;
}
static void setIsland(int y, int x, int idx) {
map[y][x] = idx;
for (int i=0; i<4; i++) {
int ny = y + dy[i];
int nx = x + dx[i];
if (ny < 0 || ny >= N || nx < 0 || nx >= M) continue;
if (map[ny][nx] != -1) continue;
setIsland(ny, nx, idx);
}
}
static int[] measure(int y, int x, int dir) {
int cy = y;
int cx = x;
int d = 0;
while (true) {
cy += dy[dir];
cx += dx[dir];
if (cy < 0 || cy >= N || cx < 0 || cx >= M) return null;
if (map[cy][cx] != 0) {
return new int[] {map[cy][cx], d};
}
d++;
}
}
}
메모리 및 시간
리뷰
- 격자 비가중치 그래프 탐색(DFS) + 정점 기반 가중치 그래프 MST 문제
- 지도 정보에서 그래프를 추출해서 재작업...
- 여러모로 귀찮은 문제다. 좋게 말하면 그래프 집대성.