문제 링크: https://www.acmicpc.net/problem/11660

 

11660번: 구간 합 구하기 5

첫째 줄에 표의 크기 N과 합을 구해야 하는 횟수 M이 주어진다. (1 ≤ N ≤ 1024, 1 ≤ M ≤ 100,000) 둘째 줄부터 N개의 줄에는 표에 채워져 있는 수가 1행부터 차례대로 주어진다. 다음 M개의 줄에는 네

www.acmicpc.net

▶문제

N×N개의 수가 N×N 크기의 표에 채워져 있다. (x1, y1)부터 (x2, y2)까지 합을 구하는 프로그램을 작성하시오. (x, y)는 x행 y열을 의미한다.

예를 들어, N = 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)까지 합을 구해 출력한다.


▶해설

(x1,y1) ~ (x2, y2)까지 모든 합을 들어올 때마다 구한다면 시간 초과가 뜰 것입니다. 

 

(1,1) ~ (a,b) 임의의 점까지 모든 합을 구합니다.

 

dp에 값(모든 합)을 채워줍니다.

private static void makeDpMap() {
    for(int i=1; i<=n; i++){ // (1,i),(i,1) 은 값을 채워줍니다.
        dp[1][i]=map[1][i]+dp[1][i-1];
        dp[i][1] = map[i][1]+dp[i-1][1];
    }

    for(int i=2; i<=n; i++){ // 중복되는 곳을 빼줍니다. 
        for(int j=2; j<=n; j++){
            dp[i][j]= dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1] + map[i][j];
        }
    }
}
1 2 3
4 5 6
7 8 9

(1,1) ~ (1,1) = 1  -> (1,1)

(1,1) ~ (1,2) = 3  -> (1,1) + (1,2)

(1,1) ~ (2,1) = 5  -> (1,1) + (2,1)

 

그렇다면 (1,1) ~ (2,2) = (2,2) + (1,1) ~ (1,2) + (1,1) ~ (2,1) - (1,1) ~ (1,1)로 구성됩니다.

 

따라서 사각형을 중점으로 구한 합에서 속하지 않는 곳을 빼고 중복되는 곳을 다시 더해주면 됩니다.

 

점화식을 구할 수 있습니다.

 

dp[i][j]-(dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1])

public class Main {
    static int n, m;
    static int [][] dp, map;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] s = br.readLine().split(" ");

        n = Integer.parseInt(s[0]);
        m = Integer.parseInt(s[1]);
        map = new int[n + 1][n + 1];
        dp = new int[n+1][n+1];
        for (int i = 1; i <= n; i++) {
            String[] s1 = br.readLine().split(" ");
            for (int j = 1; j <= n; j++) {
                map[i][j] = Integer.parseInt(s1[j - 1]);
            }
        }
        int result = 0;
        int xStart;
        int yStart;
        int xEnd;
        int yEnd;
        makeDpMap();

        for (int i = 0; i < m; i++) {
            String[] s1 = br.readLine().split(" ");
            yStart = Integer.parseInt(s1[0]);
            xStart = Integer.parseInt(s1[1]);
            yEnd = Integer.parseInt(s1[2]);
            xEnd = Integer.parseInt(s1[3]);
            result = solve(yStart,xStart,yEnd,xEnd);
            System.out.println(result);
        }

    }

    private static void makeDpMap() { // 합을 구하는 부분
        for(int i=1; i<=n; i++){
            dp[1][i]=map[1][i]+dp[1][i-1];
            dp[i][1] = map[i][1]+dp[i-1][1];
        }

        for(int i=2; i<=n; i++){
            for(int j=2; j<=n; j++){
                dp[i][j]= dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1] + map[i][j];
            }
        }

    }

    private static int solve(int yStart, int xStart, int yEnd, int xEnd){
        return dp[yEnd][xEnd]-(dp[yStart-1][xEnd]+dp[yEnd][xStart-1]-dp[yStart-1][xStart-1]);
    }
}