Algorithms
[BOJ] 15684번 사다리 조작 Java 풀이
SDev
2021. 6. 26. 07:40
728x90
DFS로 구현하다가 막혀, 다른 분의 풀이 소스를 참고했다. DFS 백트래킹으로 최솟값을 찾는 문제를 접근하면 반드시 끝까지 탐색하면서 최솟값을 갱신할 수 있으면 갱신하는 방식으로만 풀이가 가능하다고 생각했다. 그런데 모든 문제에 적용할 수는 없겠지만, 이 문제에서는 DFS를 약간 변형하는 방법으로 효율적으로 풀이를 하는 방법이 있었다.
참고: https://leveloper.tistory.com/96
풀이 구성
- void dfs(int x, int count): 가로선을 놓은 개수(count), 가로선을 놓을 지 말지 탐색할 높이(x)를 기준으로 재귀적으로 가로선을 놓고 다음 함수를 호출한 뒤, 다시 가로선을 제거하는 메서드
- boolean check(): 모든 세로선이 주어진 조건을 만족하는지 판별해주는 boolean 반환 메서드
- int[][] map: 사다리 정보 가진 2차원 배열, 1이면 오른쪽 방향 사다리를 둔 상태이고 2이면 왼쪽 방향으로 사다리를 둔 상태로 저장
- boolean finish: 현재 map을 기반으로 주어진 조건 만족하는지를 나타내는 플래그 변수
전체 코드
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
private static int n, m, h, answer;
private static int[][] map;
private static boolean finish = false;
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
n = Integer.parseInt(st.nextToken());
m = Integer.parseInt(st.nextToken());
h = Integer.parseInt(st.nextToken());
map = new int[h + 1][n + 1];
for (int i = 0; i < m; i++) {
int x, y;
st = new StringTokenizer(br.readLine());
x = Integer.parseInt(st.nextToken());
y = Integer.parseInt(st.nextToken());
// 1이면 오른쪽 방향 사다리, 2이면 왼쪽 방향 사다리
map[x][y] = 1;
map[x][y + 1] = 2;
}
// 사다리를 놓아보고 성공한다면 바로 break, 안되면 3번까지
for (int i = 0; i <= 3; i++) {
answer = i;
dfs(1, 0);
if (finish) break;
}
System.out.println((finish) ? answer : -1);
}
private static void dfs(int x, int count) {
// 답을 찾았다면 더이상 연산하지 않음
if (finish) return;
// 0 ~ 3번째 사다리를 놓은 상태에서
if (answer == count) {
// 모든 세로선이 조건을 만족한다면 종료
if (check()) finish = true;
return;
}
// 높이 depth를 1부터 h까지
for (int i = x; i < h + 1; i++) {
// 1번부터 마지막 전 세로선까지
for (int j = 1; j < n; j++) {
// 우측으로 가로선이 존재하지 않으면
if (map[i][j] == 0 && map[i][j + 1] == 0) {
// 가로선을 놓는다
map[i][j] = 1;
map[i][j + 1] = 2;
// 가로선을 놓은 상태에서 dfs 호출
dfs(i, count + 1);
// 다시 가로선 제거
map[i][j] = map[i][j + 1] = 0;
}
}
}
}
// 모든 세로선이 조건을 만족하는지 판별해주는 boolean 반환 메서드
private static boolean check() {
// i: 세로선
// y: 움직이는 위치에서의 column
for (int i = 1; i <= n; i++) {
int x = 1, y = i;
// j: 높이
// x: h번 하강하는 과정에서의 depth
for (int j = 0; j < h; j++) {
// 1이면 오른쪽 이동
if (map[x][y] == 1) y++;
// 2이면 왼쪽 이동
else if (map[x][y] == 2) y--;
// 1만큼 내려감
x++;
}
// 출발 세로선 인덱스와 도착한 세로선 인덱스가 다르면 false
if (y != i) return false;
}
// 모든 세로선의 출발 세로선 인덱스와 도착한 세로선 인덱스가 같은 경우 true
return true;
}
}