[JavaScript] 백준 분할정복(Divide&Conquer) 문제 : 2447번 별 찍기 - 10
분할정복(Divide&Conquer)이란?
분할정복(Divide & Conquer) 알고리즘은 문제를 해결하는 일반적인 방법 중 하나로, 주어진 문제를 더 작고 동일한 유형의 하위 문제로 나눈 다음, 이 하위 문제들을 해결하여 원래 문제의 해답을 구하는 방식입니다. 이 방법은 여러 가지 알고리즘 설계 기법 중에서도 특히 강력하고 널리 사용됩니다.
분할정복 알고리즘은 세 가지 주요 단계로 구성됩니다:
- 분할(Divide): 문제를 더 작고 독립적인 하위 문제로 나눕니다. 이 단계에서는 주어진 문제를 나누는 기준과 방법을 결정합니다.
- 정복(Conquer): 나눈 하위 문제들을 재귀적으로 해결합니다. 이 단계에서는 하위 문제를 해결하기 위해 다시 분할정복 알고리즘을 적용하거나, 더 이상 나눌 수 없을 만큼 작은 경우 직접 해결합니다.
- 결합(Combine): 하위 문제의 해답들을 결합하여 원래 문제의 해답을 만듭니다. 이 단계에서는 하위 문제들의 해답을 이용해 전체 문제의 해답을 도출하는 방법을 정의합니다.
DP와의 공통점 및 차이점
공통점
- 큰 문제를 더 작은 하위 문제로 나누어 해결한다.
차이점
- 분할 정복 : 하위 문제가 서로 독립적
- DP : 하위 문제들이 중복 되는 경우
알고리즘을 공부 할 수록, 서로 비슷한 알고리즘이 많다는 것을 알게 된다. 비슷하지만 다른 알고리즘들을 확실히 이해하고 각 문제에 적합한 알고리즘을 찾는 능력을 키우는 것이 중요해진다.
문제 요구사항
재귀적인 패턴으로 별을 찍어 보자. N이 3의 거듭제곱(3, 9, 27, ...)이라고 할 때, 크기 N의 패턴은 N×N 정사각형 모양이다.
크기 3의 패턴은 가운데에 공백이 있고, 가운데를 제외한 모든 칸에 별이 하나씩 있는 패턴이다.
***
* *
***
N이 3보다 클 경우, 크기 N의 패턴은 공백으로 채워진 가운데의 (N/3)×(N/3) 정사각형을 크기 N/3의 패턴으로 둘러싼 형태이다. 예를 들어 크기 27의 패턴은 예제 출력 1과 같다.
입력
첫째 줄에 N이 주어진다. N은 3의 거듭제곱이다. 즉 어떤 정수 k에 대해 N=3^k이며, 이때 1 ≤ k < 8이다.
출력
첫째 줄부터 N번째 줄까지 별을 출력한다.
입출력 예
입력 | 출력 |
27 | ![]() |
문제 이해하기
문제 설명이 자세하지 않아 문제를 이해하는데 시간이 걸렸다.
이해한 결과, 3의 제곱수인 N이 주어질 때, N은 다음과 같이 구성된다.
- N이 1일 때
*
- N이 3일 때 : 가운데에 크기가 3/3 = 1*1인 공백을 두고 3*3의 패턴이 완성된다.
***
* *
***
- N이 9일 때 : N이 3일 때의 패턴을 반복한다. 단, 가운데에 크기가 9/3 = 3*3인 공백을 두고 패턴을 그린다.
- N이 27일 때 : N이 9일 때의 패턴을 반복한다. 단, 가운데에 크기가 27/3 = 9*9인 공백을 두고 패턴을 그린다.
즉, N이 커질수록 N/3에서 사용한 패턴을 재사용하여 3X3의 패턴을 만드는데, 중앙은 빈 형태로 그리게 되는 것이다.
그럼 이 문제는 하위 패턴을 재사용한다는 점에서 DP가 아닐까 싶긴하지만, 그림이 아닌 문자열을 하나씩 찍어나가는 것이기에, DP보다는 분할정복이 더 적합하다고 볼 수 있다.
성공 코드
function solution(N) {
// 2차원 배열 생성 후 각 자리의 문자 저장 -> 마지막에 join
let pattern = Array.from({ length: N }, () => Array(N).fill(' '));
function fillPattern(x, y, size) {
if (size === 1) {
pattern[x][y] = '*';
return;
}
let newSize = size / 3;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (i === 1 && j === 1) continue; // 가운데 비우기
fillPattern(x + i * newSize, y + j * newSize, newSize);
}
}
}
fillPattern(0, 0, N);
for (let i = 0; i < N; i++) {
console.log(pattern[i].join(''));
}
}
solution(N[0]);
- 문자열을 담을 2차원 배열을 생성한다 -> N*N 배열
- fillPattern 함수를 재귀적으로 호출하며 배열을 채운다
- fillPattern(0, 0, 9)
- fillPattern(0, 0, 3) -> fillPattern(0, 0, 1) -> [0, 0] 자리에 *
- 반복하여 [ 0~2, 0~2 ] 자리에 3X3 패턴 생성
- fillPattern(0, 3, 3) -> fillPattern(0, 3, 1) -> [0, 3] 자리에 *
- 반복하여 [ 0~2, 3~5 ] 자리에 3X3 패턴 생성
- 위의 과정 반복하며 한 패턴씩 생성하여 9X9 패턴 완성
- 위의 9X9 패턴 생성 반복
- fillPattern(0, 0, 9)
- 마지막에 배열을 join함으로서 문자열 형태로 출력
지피티의 도움을 받아 문제를 풀었다. 처음에는 단순 패턴 반복이라 생각하였는데, 너무나도 인적 사고였고.. 한 줄씩 출력하며 패턴을 완성해야하는 프로세스를 구현하기가 어려웠다. 컴퓨터적 사고를 통해 알고리즘을 구현해내는 것을 더 연습해야 할 듯 하다.