알고리즘
자바 - 프로그래머스 92344 / 파괴되지 않은 건물
kdozlo
2025. 6. 3. 15:14
https://school.programmers.co.kr/learn/courses/30/lessons/92344?language=java
프로그래머스
SW개발자를 위한 평가, 교육의 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프
programmers.co.kr
lv3
풀이 방법
- 문제 이해
- N × M 보드, 각 칸마다 초기 내구도.
- K개의 스킬이 [type, r1, c1, r2, c2, degree] 형태로 주어짐.
- type = 1(적 공격) → (r1, c1) ~ (r2, c2) 구간의 내구도 degree만큼 감소
- type = 2(아군 회복) → 해당 구간 내구도 degree만큼 증가
- 스킬이 모두 끝난 뒤, 내구도가 1 이상인 칸의 개수를 세서 반환.
- 왜 단순 완전탐색(O(K·N·M))이면 안 되는가
- K ≤ 250000, N ≤ 1000, M ≤ 1000 → O(K * N * M) ≤ 2.5 × 10^11 연산 → 시간초과
- 2차원 차분 배열(diff) + 누적합 아이디어
- (N+1)×(M+1) 크기의 정수 배열 diff를 모두 0으로 초기화
- 각 스킬에 대해 “사각형 (r1 .. r2, c1 .. c2)에 +val”을 O(1)로 diff에 기록
- val = (type==1 ? -degree : +degree)
- diff[r1][c1] += val;
- diff[r1][c2+1] -= val; (c2+1 ≤ M)
- diff[r2+1][c1] -= val; (r2+1 ≤ N)
- diff[r2+1][c2+1] += val; (r2+1 ≤ N, c2+1 ≤ M)
- diff 전체에 가로 누적합(행별) 수행
- for (int i = 0; i <= N; i++) { for (int j = 1; j <= M; j++) { diff[i][j] += diff[i][j-1]; } }
- diff 전체에 세로 누적합(열별) 수행
- for (int j = 0; j <= M; j++) { for (int i = 1; i <= N; i++) { diff[i][j] += diff[i-1][j]; } }
- 이제 diff[i][j]엔 (i, j)에 최종적으로 적용된 “내구도 변화량”이 들어 있으므로,
- board[i][j] + diff[i][j] 값을 계산해서 1 이상인 칸만 카운트
- 시간 복잡도: O(K + N * M)
- 스킬 K개를 O(1)씩 기록 → O(K)
- 2차원 누적합 두 번 → O(N * M)
- 최종 카운트 O(N * M)
- → 총합 O(K + N * M)
- 결과
- Java로도 N ≤ 1000, M ≤ 1000, K ≤ 250000 범위에서 충분히 통과
코드
class Solution {
private int[][] difArray;
public int solution(int[][] board, int[][] skill) {
getDifArray(board.length, board[0].length, skill);
return getAnswer(board);
}
private void getDifArray(int r, int c, int[][] skill) {
difArray = new int[r + 1][c + 1];
for(int[] s : skill) {
int type = s[0];
int r1 = s[1];
int c1 = s[2];
int r2 = s[3];
int c2 = s[4];
int degree = s[5];
int value = type == 1 ? -degree : degree;
difArray[r1][c1] += value;
difArray[r1][c2 + 1] += -value;
difArray[r2 + 1][c1] += -value;
difArray[r2 + 1][c2 + 1] += value;
}
// col prefix sum
for(int i = 0; i <= r; i++) {
for(int j = 1; j <= c; j++) {
difArray[i][j] += difArray[i][j - 1];
}
}
// row prefix sum
for(int i = 1; i <= r; i++) {
for(int j = 0; j <= c; j++) {
difArray[i][j] += difArray[i - 1][j];
}
}
}
private int getAnswer(int[][] board) {
int count = 0;
for(int i = 0; i < board.length; i++) {
for(int j = 0; j < board[0].length; j++) {
board[i][j] += difArray[i][j];
if(board[i][j] > 0)
count++;
}
}
return count;
}
}