문제 설명
0과 1로 이루어진 2n x 2n 크기의 2차원 정수 배열 arr이 있습니다. 당신은 이 arr을 쿼드 트리와 같은 방식으로 압축하고자 합니다. 구체적인 방식은 다음과 같습니다.
- 당신이 압축하고자 하는 특정 영역을 S라고 정의합니다.
- 만약 S 내부에 있는 모든 수가 같은 값이라면, S를 해당 수 하나로 압축시킵니다.
- 그렇지 않다면, S를 정확히 4개의 균일한 정사각형 영역(입출력 예를 참고해주시기 바랍니다.)으로 쪼갠 뒤, 각 정사각형 영역에 대해 같은 방식의 압축을 시도합니다.
arr이 매개변수로 주어집니다. 위와 같은 방식으로 arr을 압축했을 때, 배열에 최종적으로 남는 0의 개수와 1의 개수를 배열에 담아서 return 하도록 solution 함수를 완성해주세요.
제한사항
- arr의 행의 개수는 1 이상 1024 이하이며, 2의 거듭 제곱수 형태를 하고 있습니다. 즉, arr의 행의 개수는 1, 2, 4, 8, ..., 1024 중 하나입니다.
- arr의 각 행의 길이는 arr의 행의 개수와 같습니다. 즉, arr은 정사각형 배열입니다.
- arr의 각 행에 있는 모든 값은 0 또는 1 입니다.
문제풀이
해당 문제는 재귀함수를 통한 분할정복법으로 해결이 가능하다. 문제의 배열의 크기가 2의 제곱수로 주어지고, 퀀텀 분할을 하는 과정들이 2의 제곱근만큼 진행되기 때문에 Ax 지점에서의 작업과 A(x-1)에서의 작업이 완전히 일치하기 때문이다. 배열의 크기를 계속 2씩 나눠가며 하부 배열들의 값들을 검사하는 방식으로 작업을 진행했다. 검사는 배열 내부에 존재하는 모든 요소들의 값이 동일한지 판별하는 검사였고, 배열의 크기가 1인 경우 배열방은 한칸이므로 검사 진행 시 반드시 true를 반환한다.
- 인풋값으로 들어오는 배열과 현재 타겟하고 있는 배열의 위치를 가리키는 정수 x, y 그리고 현재 타겟팅한 배열의 크기를 지정한다. (초기값은 x = 0, y = 0, 타겟팅한 배열의 크기 = 인풋 배열의 크기)
- recursion 메소드 내부에서 해당 배열의 요소값들이 모두 일치하는지 검사하고, 일치하지 않다면 배열을 4등분으로 나누어 각각의 등분을 다시 recursion 내부에 넣는다. (최종적으로는 배열의 크기가 1이 될텐데, 배열의 크기가 1이 되면 요소값은 하나이니 검사에서 true가 반환되어 재귀함수에서 탈출이 가능하다)
- 검사에서 성공한 요소들은 bindDataToResult 메소드로 보내 해당 요소값을 추출한 후, 문제의 해답으로 반환해야 하는 result 배열에 정보를 기입한다.
- 모든 재귀가 호출이 되어 재귀가 종료되면 데이터 저장이 완료된 result 배열을 정답으로 제출한다.
public class QurdCompression {
int[] result = new int[2];
public int[] solution(int[][] arr) {
recursion(arr, 0, 0, arr.length);
return result;
}
void recursion(int[][] arr, int x, int y, int number) {
if (inspect(arr, x, y, number)) {
bindDataToResult(arr, x, y);
return;
}
int dividedNumber = number / 2;
recursion(arr, x, y, dividedNumber);
recursion(arr, x + dividedNumber, y, dividedNumber);
recursion(arr, x, y + dividedNumber, dividedNumber);
recursion(arr, x + dividedNumber, y + dividedNumber, dividedNumber);
}
private void bindDataToResult(int[][] arr, int x, int y) {
if (arr[x][y] == 1) {
result[1] = result[1] + 1;
return;
}
result[0] = result[0] + 1;
}
boolean inspect(int[][] arr, int x, int y, int number) {
boolean isAllSame = true;
int target = arr[x][y];
for (int i = x; i < x + number; i++) {
for (int j = y; j < y + number; j++) {
if (arr[i][j] != target) {
isAllSame = false;
break;
}
}
}
return isAllSame;
}
}
느낀 점
솔직히 이 코드가 정답일지 몰랐다. 프로그래머스 문제 제출을 했을 때, 시간 초과가 발생할 거라고 생각했기 때문이었다. 코드를 보면 알 수 있겠지만 이중 for문이 쓰여서 배열이 검사되는 로직도 있고, 재귀함수도 있기 때문에 성능 측면에서 매우 취약할 것 같았다. 아마 인풋으로 들어오는 데이터가 최대 1024(2의 10제곱)이기 때문에 연산수가 상대적으로 작아서 테스트를 통과한 것 같다. 분할 정복을 하는데 재귀를 사용하지 않고, 요소값들을 검사하는데 이중 for문을 쓰지 않는 방법이 무엇이 있을지.. 한번 고민해봐야 할 것 같다.
'코딩 문제' 카테고리의 다른 글
[프로그래머스 level 3] 연속 펄스 부분 수열의 합 - Java (0) | 2023.03.18 |
---|---|
[프로그래머스 level 1] 신고 결과 받기 - Java (0) | 2022.05.01 |
[백준 1018번] 체스판 다시 칠하기 - Java (0) | 2022.02.06 |
[백준 1929번] 소수 구하기 (에라토스테네스의 체) - Java (0) | 2022.02.02 |
[프로그래머스 level 2] 다리를 지나는 트럭 - Java (0) | 2021.12.27 |