문제 풀이 날짜: 2023.05.20
포스트 작성일: 2023.05.21
* 학습 목적으로 작성하는 글입니다. 풀이가 정석적이지 못할 수도 있습니다.
문제 출처
백준 온라인 저지 1074번: Z (실버1)
키워드
분할 정복
풀이 접근법
- 입력이 2의 제곱수 꼴로 주어졌으므로 이분탐색을 이용한다.
- 주어진 배열의 크기는 2^N × 2^N이다. 만약 이것을 재귀적으로 풀고 싶다면 그 이전에 2^{N - 1} × 2^{N - 1} 크기 배열(4분의 1 사이즈)을 계산한다.
- 찾는 좌표가 몇 사분면에 있는 지를 기준으로 각기 다르게 재귀 호출을 해야 한다.
- 배열의 크기를 4분의 1로 나누어 Z모양으로 탐색하므로, 한 번의 메소드 호출에서 재귀 호출이 4회 일어난다.
- 범위를 제한하지 않고 재귀 호출을 하면 시간 초과가 발생하므로, 두 가지 처리를 해준다.
1. 재귀 호출의 범위를 설정해주어야 한다.
- 현재의 위치에 따라 호출할 방향이 결정된다. 배열을 4등분 하여, 찾고 싶은 위치가 몇 사분면에 있는지에 따라 현재 좌표를 해당 사분면으로 이동시킨다.
- 좌표를 계속 이동시키다가 배열을 더이상 쪼갤 수 없거나, 현재 위치가 목표 좌표와 같은 경우 함수를 리턴시키고 결과를 출력한다.
2. 현재 위치가 몇 번째 칸인지 별도로 계산해주어야 한다.
- 위에서 서술한 대로, 모든 칸을 하나하나 다 탐색하면서 개수를 구하면 시간 초과가 발생한다.
- 따라서, 목표 좌표가 있는 곳으로 현재 좌표를 이동시키면서, 그동안 거쳐온 칸의 수를 별도로 더해주어야 한다.
- 예를 들어서, 현재 좌표가 (0, 0)이고 1사분면에 있을 때, 이를 3사분면으로 옮겼다면 1사분면과 2사분면에 존재하는 모든 칸의 수를 더해주어야 목표 좌표까지 다다르는 데 존재하는 칸의 수를 구할 수 있다.
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
private static int R;
private static int C;
private static int SIZE;
private static int count = 0;
public static void main(String[] args) throws NumberFormatException, IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine(), " ");
int N = Integer.parseInt(st.nextToken());
R = Integer.parseInt(st.nextToken());
C = Integer.parseInt(st.nextToken());
SIZE = (int) Math.pow(2, N); //한 변의 크기
recursion(SIZE, R, C);
System.out.println(count);
}
private static void recursion(int size, int r, int c) {
if(size == 1) {
return;
}
int half = size / 2;
if(r < half && c < half) { //1사분면: row와 col이 모두 key보다 크면 호출
recursion(half, r, c);
} else if(r < half && c >= half) { //2사분면: R보다 row가 크면 호출
count += size * size / 4; //1사분면의 수 skip
recursion(half, r, c - half);
} else if(r >= half && c < half) { //3사분면: C보다 col이 크면 호출
count += (size * size / 4) * 2; //2사분면의 수 skip
recursion(half, r - half, c);
} else { //4사분면: row와 col이 모두 key보다 작거나 같으면 호출
count += (size * size / 4) * 3; //3사분면의 수 skip
recursion(half, r - half, c - half);
}
}
}
재귀 호출의 방향을 설정하는 데 어려움을 겪어, 다른 블로그를 참고하여 풀었다. 이와 유사한 matrix 분할정복 문제도 모두 좌표를 밀어주면서 계산한다.
참고한 링크
https://mygumi.tistory.com/284
https://wiselog.tistory.com/133
git 링크
(git 링크 첨부)
'Study > Problem Solving' 카테고리의 다른 글
[백준 / Java] 2252번: 줄 세우기 (골드3) (0) | 2023.08.23 |
---|---|
[백준 / Java] 1987번: 알파벳 (골드4) (0) | 2023.08.23 |
[SWEA / Java] 9843번: 촛불 이벤트 (D5) (0) | 2023.08.21 |
[백준 / Java] 15686번: 치킨 배달 (골드5) (0) | 2023.08.21 |
[SWEA / Java] 6808번: 규영이와 인영이의 카드게임 (D3) (1) | 2023.08.21 |