처음에는 단순히 Union-Find를 떠올렸지만, 방향이 있는 그래프이고 사이클을 감지해야 하므로 DFS를 사용하여 방문 상태를 관리하는 것이 더 직관적이라 판단했습니다.
Visited vs Finished이 문제의 핵심은 "이미 방문한 노드를 만났을 때"의 처리입니다.
모든 칸을 순회하며 방문하지 않은 곳에서 DFS를 시작합니다.
이동하다가 이미 방문한 칸(visited == true)을 만났을 때 두 가지 경우가 있습니다.
cycle == true)을 만남 기존 집합에 합류하는 경로임. (정답 증가 X)이 두 가지를 구분하기 위해 방문 배열(visit) 외에 탐색 완료 배열(cycle)이 하나 더 필요합니다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static StringTokenizer st;
static int N, M;
static char[][] A;
static boolean[][] visit; // 방문 여부 체크
static boolean[][] finished; // 사이클(탐색) 완료 여부 체크
static int answer = 0;
// 방향 처리를 위한 배열 (U, D, L, R)
static final int[] dy = {-1, 1, 0, 0};
static final int[] dx = {0, 0, -1, 1};
public static void main(String[] args) throws IOException {
// 1. 입력 처리
st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
M = Integer.parseInt(st.nextToken());
A = new char[N][M];
for (int i = 0; i < N; i++) {
A[i] = br.readLine().toCharArray();
}
visit = new boolean[N][M];
finished = new boolean[N][M];
// 2. 전체 맵을 순회하며 DFS 수행
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (!visit[i][j]) {
dfs(i, j);
}
}
}
System.out.println(answer);
}
private static void dfs(int y, int x) {
visit[y][x] = true; // 현재 노드 방문 처리
int d = dir(A[y][x]);
int ny = y + dy[d];
int nx = x + dx[d];
// 다음 노드를 아직 방문하지 않았다면 계속 탐색
if (!visit[ny][nx]) {
dfs(ny, nx);
}
// 다음 노드를 방문은 했지만, 아직 탐색 처리가 끝나지 않았다면?
// -> 이번 탐색 경로에서 만난 것이므로 '사이클'이다!
else if (!finished[ny][nx]) {
answer++;
}
// 현재 노드의 탐색 종료 처리 (이후 다른 경로에서 이 노드를 만나면 사이클이 아님)
finished[y][x] = true;
}
// 문자를 방향 인덱스로 변환하는 헬퍼 메서드
private static int dir(Character c) {
if (c == 'U') return 0;
if (c == 'D') return 1;
if (c == 'L') return 2;
return 3;
}
}
이 문제의 승부처는 "방문했던 곳을 다시 왔을 때, 이게 '새로운 사이클'인지 '기존 경로에 합류'하는 것인지 구분하는 것"이었습니다. 이를 위해 visited 배열 하나만 쓰는 것이 아니라, cycle 배열을 추가로 사용하여 상태를 관리하는 기법이 유효했습니다.
처음에는 visit 배열 하나만 있으면 해결될 것이라 생각했습니다.
[오답 코드의 논리]
"메인 루프(
for i, for j)에서dfs가 호출되는 횟수가 곧 독립된 집합의 개수(Safe Zone 개수)가 아닐까?"
// 실패했던 코드 로직의 일부
if (!visit[i][j]) {
safe += 1; // 호출할 때마다 무조건 카운트 증가
dfs(i, j);
}
[문제점]
이 로직은 "새로운 경로가 기존에 이미 발견된 사이클로 합류하는 경우"를 고려하지 못합니다.
(0,0)에서 시작해 사이클을 하나 찾아서 safe=1이 되었습니다.(1,0)은 아직 방문 안 했지만, 따라가다 보니 (0,0) 쪽의 사이클로 들어갑니다.safe를 또 증가시켜 2가 됩니다.