(왼쪽 숫자, 오른쪽 숫자)
로 구성된 도미노 조각.타일이 2칸으로 구성되어 있으니, 각 칸을 방문하며 BFS를 돌면 되지 않을까?
같은 숫자라면 이동 가능하니, map[x][y] == map[nx][ny] 조건만 걸면 될 것 같은데?
(x, y)
만 보고 주변 탐색을 하다보니 같은 타일에 있는 두 칸을 함께 처리하지 못함(1,1)-(1,2)
라면 (1,1)
을 방문해도 (1,2)
를 자동으로 방문처리하지 않음getTileNum(x, y)
함수로 현재 좌표가 속한 타일 번호를 빠르게 구할 수 있도록 구현beforePoint[tileNum] = prevTileNum
방식으로 경로 추적맵 구성
BFS 시작
같은 타일 내부 이동
도달 불가능 시 가장 큰 번호의 타일에 도달하도록 경로 저장
결과 출력
beforePoint[]
배열로 경로 역추적예제 입력:
5
1 4
4 5
3 4
5 4
5 2
4 2
5 6
4 4
6 5
2 4
5 1
6 1
1 6
2 3
4 2
5 3
1 2
5 5
4 1
2 2
4 3
2 3
3 4
예제 출력:
7
1 2 7 12 17 22 23
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.Stack;
import java.util.StringTokenizer;
public class Main {
private static StringTokenizer st;
private static StringBuilder answerString = new StringBuilder();
private static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
private static int N, M;
private static int[][] map;
private static boolean[][] visited;
private static int[][] dir = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
private static int[] beforePoint;
private static int totalCnt;
public static void main(String[] args) throws IOException {
inputSetting();
solution();
System.out.println(answerString);
}
private static void solution() {
Queue<int[]> queue = new ArrayDeque<>();
int maxTileNum = 1;
queue.add(new int[]{0, 0});
queue.add(new int[]{0, 1});
visited[0][0] = visited[0][1] = true;
while (!queue.isEmpty()) {
int[] cur = queue.poll();
int x = cur[0];
int y = cur[1];
int curTileNum = getTileNum(x, y);
maxTileNum = Math.max(maxTileNum, curTileNum);
for (int i = 0; i < 4; i++) {
int dx = x + dir[i][0];
int dy = y + dir[i][1];
if (dx < 0 || dx >= N || dy < 0 || dy >= M) continue;
if (visited[dx][dy] || map[dx][dy] <= 0) continue;
int nextTileNum = getTileNum(dx, dy);
if (map[x][y] != map[dx][dy]) continue;
visited[dx][dy] = true;
queue.add(new int[]{dx, dy});
// 같은 타일의 다른 면도 같이 방문
if (dy - 1 >= 0 && getTileNum(dx, dy - 1) == nextTileNum && !visited[dx][dy - 1]) {
visited[dx][dy - 1] = true;
queue.add(new int[]{dx, dy - 1});
} else if (dy + 1 < M && getTileNum(dx, dy + 1) == nextTileNum && !visited[dx][dy + 1]) {
visited[dx][dy + 1] = true;
queue.add(new int[]{dx, dy + 1});
}
if (beforePoint[nextTileNum] == 0) {
beforePoint[nextTileNum] = curTileNum;
}
}
}
Stack<Integer> stack = new Stack<>();
int cnt = 1;
while (maxTileNum != 1) {
stack.add(maxTileNum);
maxTileNum = beforePoint[maxTileNum];
cnt++;
}
stack.add(1);
answerString.append(cnt).append("\n");
while (!stack.isEmpty()) {
answerString.append(stack.pop()).append(" ");
}
}
private static int getTileNum(int x, int y) {
if (x % 2 == 1) {
return ((x - 1) / 2) * (2 * N - 1) + N + ((y - 1) / 2) + 1;
} else {
return (x / 2) * (2 * N - 1) + (y / 2) + 1;
}
}
private static void inputSetting() throws IOException {
N = Integer.parseInt(br.readLine());
M = 2 * N;
map = new int[N][M];
visited = new boolean[N][M];
beforePoint = new int[(N + N - 1) * (N / 2) + (N % 2) * N + 1];
int total = (N + N - 1) * (N / 2) + (N % 2) * N;
int tmp = 0;
for (int i = 1; i < N; i += 2) {
map[i][0] = map[i][M - 1] = -1;
visited[i][0] = visited[i][M - 1] = true;
}
for (int i = 0; i < total; i++) {
st = new StringTokenizer(br.readLine());
while (map[tmp / M][tmp % M] == -1) tmp++;
map[tmp / M][tmp % M++] = Integer.parseInt(st.nextToken());
map[tmp / M][tmp % M++] = Integer.parseInt(st.nextToken());
}
}
}
이 문제를 풀면서 타일 단위의 경로 탐색이 낱개 탐색과는 본질적으로 다르다는 점을 확실히 느꼈다.
특히 타일 구조상 BFS 구현이 단순 숫자 비교만으로는 부족하다는 것을 깨달았고,
"타일 내부 연결 보장" + "타일 간 공유면 숫자 일치"라는 조건을 잘 반영하는 것이 핵심이었다.