1. 문제 분석
1.1 문제 요약
- N x M 크기의 2차원 건물 배열이 주어집니다.
- 각 건물은 내구도를 가지고 있습니다.
- 여러 개의 스킬(공격/회복)이 직사각형 영역에 적용됩니다.
- 최종적으로 내구도가 1 이상인 건물의 개수를 구해야 합니다.
1.2 제한사항 분석
// 입력 크기
1 ≤ board의 행/열 길이 ≤ 1,000
1 ≤ skill의 행의 길이 ≤ 250,000
1 ≤ degree ≤ 500
- N: 건물의 행 길이
- M: 건물의 열 길이
- K: 스킬의 개수
2. 브루트 포스 접근
2.1 단순 구현
class Solution {
public int solutionBruteForce(int[][] board, int[][] skills) {
int answer = 0;
int height = board.length;
int width = board[0].length;
// 각 스킬 순회
for (int[] skill : skills) {
int type = skill[0];
int r1 = skill[1], c1 = skill[2];
int r2 = skill[3], c2 = skill[4];
int degree = type == 1 ? -skill[5] : skill[5];
// 영향 받는 모든 칸 갱신
for (int i = r1; i <= r2; i++) {
for (int j = c1; j <= c2; j++) {
board[i][j] += degree;
}
}
}
// 살아남은 건물 카운트
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
if (board[i][j] > 0) answer++;
}
}
return answer;
}
}
2.2 시간 복잡도 문제
- O(K × N × M)
- 각 스킬마다 최대 N × M 칸 순회
- 각 스킬마다 최대 1,000 × 1,000 칸 순 회
- 최악의 경우: O(250,000 × 1,000 × 1,000)
- 시간 초과 발생!
3. 차분 배열을 활용한 최적화
- 차분 배열이란 구간 업데이트를 효율적으로 처리하기 위한 기법입니다.
- 차분 배열을 활용하면 구간 업데이트를 O(1)에 처리할 수 있습니다.
- DifferenceArray.md 참고
3.1 핵심 아이디어
- 차분 배열에 변화량만 기록: O(1)
- 누적합으로 최종 상태 계산: O(N × M)
- 원본 배열과 합산하여 결과 도출: O(N × M)
- 총 시간 복잡도: O(K + N × M)
3.2 최적화된 구현
class Solution {
public int solution(int[][] board, int[][] skills) {
int answer = 0;
int height = board.length;
int width = board[0].length;
// 차분 배열 초기화 (원본보다 1 크게)
int[][] differenceArray = new int[height + 1][width + 1];
// 1. 차분 배열에 변화량 기록: O(K)
for (int[] skill : skills) {
int type = skill[0];
int r1 = skill[1], c1 = skill[2];
int r2 = skill[3], c2 = skill[4];
int degree = type == 1 ? -skill[5] : skill[5];
// 차분 배열의 적절한 위치에 값 기록
differenceArray[r1][c1] += degree; // 시작점
differenceArray[r1][c2 + 1] -= degree; // 우측 경계
differenceArray[r2 + 1][c1] -= degree; // 하단 경계
differenceArray[r2 + 1][c2 + 1] += degree;// 대각선 경계
}
// 2. 2차원 누적합 계산: O(N × M)
for (int i = 0; i <= height; i++) {
for (int j = 0; j <= width; j++) {
if (i > 0) differenceArray[i][j] += differenceArray[i - 1][j];
if (j > 0) differenceArray[i][j] += differenceArray[i][j - 1];
if (i > 0 && j > 0) differenceArray[i][j] -= differenceArray[i - 1][j - 1];
}
}
// 3. 최종 결과 계산: O(N × M)
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
if (board[i][j] + differenceArray[i][j] > 0) answer++;
}
}
return answer;
}
}