2차원 누적합: 이해, 생성, 활용

1. 2차원 누적합이란?

누적합 배열 P는 원본 배열 A의 (0,0)부터 (i, j)까지의 합을 저장합니다.
즉,

  • P[i][j] = A[0][0] + A[0][1] + … + A[i-1][j-1] (패딩 때문에 인덱스가 1씩 밀림)

패딩을 적용하면 *P[0][]P[][0]를 모두 0으로 채워서,
계산할 때 경계 조건을 신경쓰지 않아도 됩니다.
예를 들어, 누적합 배열의 크기는 (n+1)×(n+1)이 되어,
실제 원본 배열의 값은 P[1][1]부터 P[n][n]에 저장됩니다.


2. 누적합 배열 생성

2.1 왜 패딩을 사용할까?

패딩을 사용하면

  • 첫 행첫 열에 대해 별도의 조건문 없이 계산할 수 있습니다.
  • 인덱스 접근 시 “범위 초과” 문제를 걱정할 필요가 없으므로 코드가 간결해집니다.

2.2 누적합 배열 공식

패딩을 적용한 누적합 배열 P를 만드는 공식은 다음과 같습니다.

P[i][j] = A[i-1][j-1] + P[i-1][j] + P[i][j-1] - P[i-1][j-1]   (for i, j ≥ 1)
  • A[i-1][j-1]: 원본 배열의 현재 값
  • P[i-1][j]: 위쪽 누적합
  • P[i][j-1]: 왼쪽 누적합
  • P[i-1][j-1]: 위쪽과 왼쪽이 겹치는 영역 (중복 제거용)

2.3 예시: 5×5 배열 누적합 만들기

원본 배열 A

0 1 2 3 4
0 3 1 9 2 3
1 9 2 3 8 1
2 2 3 1 6 3
3 5 6 6 8 5
4 8 5 2 3 7

패딩된 누적합 배열 P

0 1 2 3 4 5
0 0 0 0 0 0 0
1 0 3 4 13 15 18
2 0 12 15 27 37 41
3 0 14 20 33 49 56
4 0 19 31 50 74 86
5 0 27 44 65 92 111

계산 예시:
P[1][3] 계산
– A[0][2] = 9
– P[0][3] = 0 (패딩된 첫 행)
– P[1][2] = 4
– P[0][2] = 0

따라서, P[1][3] = 9 + 0 + 4 – 0 = 13


3. 구간 합 계산: 공식의 의미와 사용법

누적합 배열을 사용하면, 원본 배열 A의 임의의 직사각형 영역의 합을 아래 공식으로 O(1)에 계산할 수 있습니다.

구간 합 = P[x2][y2] - P[x1-1][y2] - P[x2][y1-1] + P[x1-1][y1-1]

3.1 각 항의 의미

  1. P[x2][y2]

(1,1)부터 (x2,y2)까지 전체 합

  1. P[x1-1][y2]

(1,1)부터 (x1-1, y2)까지의 합 → 구간의 위쪽 불필요 영역 제거

  1. P[x2][y1-1]

(1,1)부터 (x2, y1-1)까지의 합 → 구간의 왼쪽 불필요 영역 제거

  1. P[x1-1][y1-1]

위쪽과 왼쪽 영역에서 중복 제거된 겹치는 부분 → 보충

예시:
(2,2)부터 (4,4) 구간을 구한다고 할 때,
P[4][4]: (1,1)부터 (4,4)까지 전체 합
P[1][4]: (1,1)부터 (1,4)까지의 위쪽 영역 (제거)
P[4][1]: (1,1)부터 (4,1)까지의 왼쪽 영역 (제거)
P[1][1]: 중복 제거된 영역 → 보충

예를 들어, 직접 계산한 구간 합이 43이라면 위 공식으로도 43이 나옵니다.


4. 코드 구현 예시

문제( 1 2 3 4 2 3 4 5 3 4 5 6 4 5 6 7

여기서 (2, 2)부터 (3, 4)까지 합을 구하면 3+4+5+4+5+6 = 27이고, (4, 4)부터 (4, 4)까지 합을 구하면 7이다.

표에 채워져 있는 수와 합을 구하는 연산이 주어졌을 때, 이를 처리하는 프로그램을 작성하시오.

입력

첫째 줄에 표의 크기 N과 합을 구해야 하는 횟수 M이 주어진다. (1 ≤ N ≤ 1024, 1 ≤ M ≤ 100,000) 둘째 줄부터 N개의 줄에는 표에 채워져 있는 수가 1행부터 차례대로 주어진다. 다음 M개의 줄에는 네 개의 정수 x1, y1, x2, y2 가 주어지며, (x1, y1)부터 (x2, y2)의 합을 구해 출력해야 한다. 표에 채워져 있는 수는 1,000보다 작거나 같은 자연수이다. (x1 ≤ x2, y1 ≤ y2)

출력

총 M줄에 걸쳐 (x1, y1)부터 (x2, y2)까지 합을 구해 출력한다.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    public static void main(String[] args) throws IOException {
        // 입력 처리: 첫 줄에 배열의 크기 n과 구간 합 쿼리 수 m이 주어진다.
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] tokens = br.readLine().split(" ");
        int n = Integer.parseInt(tokens[0]);
        int m = Integer.parseInt(tokens[1]);

        // 원본 배열 A (0-index 기준) 입력 받기
        int[][] A = new int[n][n];
        for (int i = 0; i < n; i++) {
            tokens = br.readLine().split(" ");
            for (int j = 0; j < n; j++) {
                A[i][j] = Integer.parseInt(tokens[j]);
            }
        }

        // 누적합 배열 P 생성 (패딩 적용: 크기 (n+1) x (n+1), P[0][*]와 P[*][0]는 0)
        int[][] P = new int[n+1][n+1];
        // P 배열은 1-index부터 값이 채워진다. 즉, 원본 배열 A의 A[i][j]는 P[i+1][j+1]에 반영됨.

        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                P[i][j] = A[i-1][j-1] + P[i-1][j] + P[i][j-1] - P[i-1][j-1];
            }
        }

        // M개의 구간 합 쿼리 처리 (쿼리 좌표는 1-index로 주어진다)
        StringBuilder sb = new StringBuilder();
        for (int k = 0; k < m; k++) {
            tokens = br.readLine().split(" ");
            // 입력 좌표는 1-index이므로 그대로 사용 (패딩 배열 P에 맞춰 계산)
            int x1 = Integer.parseInt(tokens[0]);
            int y1 = Integer.parseInt(tokens[1]);
            int x2 = Integer.parseInt(tokens[2]);
            int y2 = Integer.parseInt(tokens[3]);

            int result = P[x2][y2] - P[x1-1][y2] - P[x2][y1-1] + P[x1-1][y1-1];
            sb.append(result).append("\n");
        }
        System.out.print(sb);
    }
}

코드 상세 설명

  1. 입력 처리:

첫 줄에서 n과 m을 읽어오고,
이후 n개의 줄에 걸쳐 원본 배열 A를 0-index 방식으로 입력받습니다.

  1. 누적합 배열 P 생성 (패딩 적용):

배열 P의 크기는 (n+1)×(n+1)이며,
P[0][
]와 P[*][0]는 0으로 초기화되어 경계 처리를 간단하게 만듭니다.
for문을 돌며, P[i][j]를 A[i-1][j-1]과 위쪽, 왼쪽, 대각선 위쪽 값을 이용해 계산합니다.

  1. 구간 합 쿼리 처리:

입력된 쿼리 좌표는 1-index로 주어지므로,
누적합 공식 (P[x2][y2] – P[x1-1][y2] – P[x2][y1-1] + P[x1-1][y1-1])를 그대로 적용하여 결과를 계산합니다.

참고

https://www.youtube.com/watch?v=KT864Aa3zE0

위로 스크롤