2543번 - 타일 채우기
문제 풀이 시간 : 1시간 30분
문제 요약
- 크기의 정사각형 바닥과 타일을 놓을 수 없는 배수구의 위치 가 주어짐
- 배수구를 제외한 나머지 모든 칸을 4가지 종류의 'ㄱ'자 모양 타일로 빈틈없이 채워야 함
- 각 칸에 채워진 타일의 종류를 규칙에 맞는 번호로 출력
문제 풀이
이 문제를 처음 봤을 때는 규칙을 찾는 것이 막막해 한참을 고민했다. 단순히 타일을 하나씩 끼워 맞추는 그리디 방식이나 완전 탐색으로는 경우의 수가 너무 많아 불가능해 보였다.
하지만 자세히 보다 보니 재귀적인 패턴이 보였다. 전체 바닥을 4등분 했을 때, 배수구가 있는 사분면은 '구멍이 하나 있는 정사각형'이 된다. 그렇다면 배수구가 없는 나머지 3개의 사분면은 어떻게 해야 할까?
👉 "가운데에 타일을 하나 놓아버리자!"
배수구가 없는 3개 사분면이 맞닿아 있는 정중앙에 타일 하나를 배치하면, 나머지 3개 구역도 각각 '구멍이 하나씩 생긴 작은 정사각형' 상태가 된다. 즉, 문제를 동일한 형태의 작은 문제 4개로 쪼갤 수 있게 된다.
🧩 핵심 로직 - 분할 정복
이 문제는 전형적인 분할 정복 문제로, 다음 과정을 반복한다.
- 4분할
- 배수구 위치 파악
- 중앙 타일 배치
최종 코드
import java.io.*;
import java.util.*;
public class Main {
public static int[][] arr;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
// N: 맵의 크기 (2의 제곱수)
int n = Integer.parseInt(st.nextToken());
arr = new int[n][n];
st = new StringTokenizer(br.readLine());
// 배수구의 위치 (행, 열)
int x = Integer.parseInt(st.nextToken());
int y = Integer.parseInt(st.nextToken());
// 분할 정복 시작
// 초기 맵 전체를 대상으로 배수구는 x, y에 있음
find(0, 0, x, y, n);
// 결과 출력
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
}
// x, y: 현재 사각형의 왼쪽 위 시작 좌표
// tx, ty: 배수구(구멍)의 좌표
// size: 현재 사각형의 한 변의 길이
public static void find(int x, int y, int tx, int ty, int size) {
if (size == 1)
return;
int nextSize = size / 2;
int mx = x + nextSize; // 중간 행 좌표
int my = y + nextSize; // 중간 열 좌표
// 배수구가 어느 사분면에 있는지에 따라 중앙에 놓을 타일의 모양(kind) 결정
int kind = -1;
if (tx < mx && ty < my) kind = 1; // 1사분면 (왼쪽 위)에 배수구 -> 1번 타일
else if (tx < mx && ty >= my) kind = 2; // 2사분면 (오른쪽 위)에 배수구 -> 2번 타일
else if (tx >= mx && ty < my) kind = 3; // 3사분면 (왼쪽 아래)에 배수구 -> 3번 타일
else kind = 4; // 4사분면 (오른쪽 아래)에 배수구 -> 4번 타일
// 1. 왼쪽 위 구역 처리
if (tx < mx && ty < my) {
find(x, y, tx, ty, nextSize); // 배수구 존재: 그대로 재귀
} else {
arr[mx - 1][my - 1] = kind; // 배수구 없음: 중앙 모서리 채우기
find(x, y, mx - 1, my - 1, nextSize); // 채운 곳을 배수구로 보고 재귀
}
// 2. 오른쪽 위 구역 처리
if (tx < mx && ty >= my) {
find(x, my, tx, ty, nextSize);
} else {
arr[mx - 1][my] = kind;
find(x, my, mx - 1, my, nextSize);
}
// 3. 왼쪽 아래 구역 처리
if (tx >= mx && ty < my) {
find(mx, y, tx, ty, nextSize);
} else {
arr[mx][my - 1] = kind;
find(mx, y, mx, my - 1, nextSize);
}
// 4. 오른쪽 아래 구역 처리
if (tx >= mx && ty >= my) {
find(mx, my, tx, ty, nextSize);
} else {
arr[mx][my] = kind;
find(mx, my, mx, my, nextSize);
}
}
}함께 읽으면 좋은 글
1183번 - 동전 자판기
물건의 가격 W와 6종류의 동전(500, 100, 50, 10, 5, 1원)의 보유 개수가 주어짐 가지고 있는 동전을 조합하여 물건 값을 정확히 지불해야 함 이때, 사용하는 동전의 총 개수를 최대로 만드는 방법을 구하는 문제 이 문제를 처음 봤을 때는 문제를 잘못 읽어 최소…
택배상자
order 배열은 택배 기사님이 원하는 상자 적재 순서다 기존 컨테이너 벨트는 1번부터 순서대로만 꺼낼 수 있다 보조 벨트는 스택처럼 마지막에 넣은 것부터 꺼낼 수 있다 원하는 순서대로 최대한 실을 수 있는 상자 개수를 구하는 문제다 처음에는 기존 벨트를 실제 리스트로 만들어…
롤케이크 자르기
topping 배열은 롤케이크에 일렬로 올라간 토핑 번호다 한 지점을 잘라 두 조각으로 나눴을 때, 양쪽 토핑 종류 수가 같아야 공평하게 나눈 것이다 공평하게 자를 수 있는 방법의 수를 구하는 문제다 처음에는 자르는 위치마다 왼쪽과 오른쪽 배열을 나눠서 각각 서로 다른 토핑…
할인 행사
want, number 배열로 정현이가 원하는 제품과 수량을 표현한다 discount 배열은 XYZ 마트가 매일 할인하는 제품 목록이다 연속된 10일 동안 할인 제품 종류와 수량이 want, number와 정확히 일치해야 회원가입할 수 있다 그런 시작일이 총 몇 번 있는지…