📌 문제 링크 - https://www.acmicpc.net/problem/1035
문제
최대 5개의 조각이 있는 5×5 크기의 보드가 있다. 김지민은 조각을 적절히 움직여서 모든 조각이 연결 요소를 이루게 하려고 한다. 즉 상하좌우로 인접한 조각을 모두 연결했을 때, 모든 쌍의 조각이 적어도 하나의 경로로 연결되어 있어야 한다.
한 번의 이동으로 하나의 조각을 상하좌우로 인접한 칸으로 옮길 수 있다. 보드의 상태가 주어질 때, 최소 몇 번 이동해야 모든 조각이 연결 요소를 이루게 되는지 구하는 프로그램을 작성하시오.
입력
첫째 줄부터 다섯째 줄까지 보드의 상태가 주어진다. 빈 곳은 '.'이고, 조각은 '*'이다. 조각은 1개 이상 5개 이하이다.
출력
첫째 줄에 문제의 정답을 출력한다.
풀이
백준 2314. 이세계 게임과 비슷한 문제다.
5x5 보드에 최대 5개의 조각(*)이 있다.
입력받을 때 이 조각의 위치를 비트마스크로 저장해 주고(origin) 조각 개수를 카운트해준다.(pieceCnt)
for(int r=0; r<N; r++) {
char[] row = br.readLine().toCharArray();
for(int c=0; c<N; c++) {
if(row[c] == '*') {
origin |= 1 << (r * N + c);
pieceCnt++;
}
}
}
이후 비트마스킹으로 저장한 상태를 가지고 bfs를 통해 모든 조각들이 연결될 수 있도록 최소 이동 횟수를 구해주면 된다.
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Queue;
// https://www.acmicpc.net/problem/1035
public class Main_1035 {
private static final int N = 5, TOTAL = 25;
private static final int[] dr = {-1, 1, 0, 0};
private static final int[] dc = {0, 0, -1, 1};
private static int origin; // 보드의 초기 상태
private static int pieceCnt; // 조각 개수
public static void main(String[] args) throws IOException {
init();
int cnt = getCnt();
System.out.print(cnt);
}//main
private static int getCnt() {
Queue<int[]> moveQ = new LinkedList<>(); // 조각 이동
Queue<int[]> checkQ = new LinkedList<>(); // 조각 연결 확인
boolean[] visited = new boolean[1 << TOTAL]; // 25비트의 모든 경우 저장
moveQ.offer(new int[] {origin, 0}); // 현재 상태, 이동 횟수
visited[origin] = true;
int[] cur;
int r, c, status, moveCnt;
while(!moveQ.isEmpty()) {
cur = moveQ.poll();
status = cur[0]; // 현재 상태
moveCnt = cur[1]; // 이동 횟수
// 모든 조각이 연결되었는지 확인
if(isComplete(status, checkQ)) {
return moveCnt;
}
for(int i=0, m=1; i<TOTAL; i++, m<<=1) {
if((status & m) == 0) continue; // 현재 칸이 조각이 아님
r = i / N; // 행
c = i % N; // 열
for(int d=0; d<4; d++) {
int nr = r + dr[d];
int nc = c + dc[d];
// 범위를 벗어남
if(rangeCheck(nr, nc)) continue;
// 다음칸으로 이동 가능한지 확인
int nm = 1 << (nr * N + nc);
if((status & nm) != 0) continue; // 다음 칸에 이미 조각이 있음
// 다음칸으로 이동
int next = (status ^ m) | nm;
if(visited[next]) continue;
visited[next] = true;
moveQ.offer(new int[] {next, moveCnt + 1});
}
}
}
return -1;
}//getCnt
private static boolean isComplete(int status, Queue<int[]> q) {
int r = 0, c = 0;
// 첫 번째 조각 위치 찾기
for(int i=0, m=1; i<TOTAL; i++, m<<=1) {
if ((status & m) == 0) continue;
r = i / N; // 행
c = i % N; // 열
break;
}
// 첫 번째 조각과 상하좌우로 인접한 조각 개수 구하기
int cnt = bfs(r, c, status, q);
return cnt == pieceCnt;
}//isComplete
private static int bfs(int r, int c, int status, Queue<int[]> q) {
boolean[][] visited = new boolean[N][N]; // 방문 체크
int cnt = 1; // 인접한 조각 개수
q.offer(new int[] {r, c});
visited[r][c] = true;
int[] cur;
while(!q.isEmpty()) {
cur = q.poll();
r = cur[0]; // 행
c = cur[1]; // 열
for(int i=0; i<4; i++) {
int nr = r + dr[i];
int nc = c + dc[i];
int next = (nr * N + nc);
if(rangeCheck(nr, nc) || visited[nr][nc]) continue;
if(((status >> next) & 1) == 0) continue;
cnt++;
visited[nr][nc] = true;
q.offer(new int[] {nr, nc});
}
}
return cnt;
}//bfs
private static boolean rangeCheck(int r, int c) {
return r < 0 || r >= N || c < 0 || c >= N;
}//rangeCheck
private static void init() throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
for(int r=0; r<N; r++) {
char[] row = br.readLine().toCharArray();
for(int c=0; c<N; c++) {
if(row[c] == '*') {
origin |= 1 << (r * N + c);
pieceCnt++;
}
}
}
br.close();
}//init
}//class
'Algorithm > 백준' 카테고리의 다른 글
[Java] 백준 3687. 성냥개비 (1) | 2025.04.23 |
---|---|
[Java] 백준 14907. 프로젝트 스케줄링 (0) | 2025.04.02 |
[Java] 백준 25307. 시루의 백화점 구경 (0) | 2025.03.25 |
[JAVA] 백준 14948. 군대탈출하기 (0) | 2025.03.21 |
[Java] 백준 2758. 로또 (0) | 2025.03.10 |