문제 : https://www.acmicpc.net/problem/25826
[ 문제 요약 ]
- 정사각형과 그 안에 있는 값이 주어지고, 두 점이 주어지면, 해당 구간 안에 숫자들을 지속적으로 더해 나갑니다. 그리고 마지막에 한번 원하는 위치의 두 점이 주어지면, 그 안의 값들을 모두 더한 후 출력합니다.
[ 테스트케이스 설명 ]
4 3// 사각형크기(1<=1,000), 질의 수(1<=300,000)
1 1 1 1// 크기만큼 사각형의 기본값이 주어짐(1<=1,000,000)
1 1 1 1
1 1 1 1
1 1 1 1
1 0 0 1 1 1// 1번 쿼리 : 왼쪽위, 오른쪽아래가 주어지며 행, 열 순으로 주어짐
1 0 0 2 2 2
2 0 0 2 2// 2번 쿼리 : 2번쿼리는 딱 한번만 나오며 해당 격자안의 값만 더해서 출력[ 알고리즘 분류 ]
[ 알고리즘 분류 ]
- 누적 합
- 스위핑
- 차분 배열 트릭
[ 문제 해설 ]
이 문제는 1차원 차분 배열을 2차원 차분 배열로 응용만 하면 되는 간단한 문제입니다.
두 점을 알 때, 그 격자 안에 있는 모든 위치에 숫자를 더 할 때는, 더하려는 숫자를 4개의 격에만 양, 음수로 찍어두고, for 문을 돌려 가로로 먼저 더하고, 그 후 세로로 한 번 더 더해주면 됩니다. 아래는 표로 예시를 보여주며, 6*6 격자에 (2, 2)부터 (5,5) 안에 있는 모든 격자에 5를 더하는 것을 3단계로 나눠 보여줍니다.
(1) 단계 : 4개의 점에 마킹을 합니다. 시작점(2,2)에는 +5를, 시작점 오른쪽에 끝나는 곳에 -5를, 시작점 밑에 끝나는 곳에 -5를, 그 오른쪽에 5를 찍어줍니다. 이렇게 해야 가로 누적합을 하고 세로 누적합을 할 때 값이 상쇄됩니다.
y , x | 1 | 2 | 3 | 4 | 5 | 6 |
1 | ||||||
2 | 5 | -5 | ||||
3 | ||||||
4 | ||||||
5 | ||||||
6 | -5 | 5 |
(2)단계 : 가로로 모두 더함
y , x | 1 | 2 | 3 | 4 | 5 | 6 |
1 | ||||||
2 | 5 | 5 | 5 | 5 | 0 | |
3 | ||||||
4 | ||||||
5 | ||||||
6 | -5 | -5 | -5 | -5 | 0 |
(3) 단계 : 세로로 모두 더함
y , x | 1 | 2 | 3 | 4 | 5 | 6 |
1 | ||||||
2 | 5 | 5 | 5 | 5 | 0 | |
3 | 5 | 5 | 5 | 5 | 0 | |
4 | 5 | 5 | 5 | 5 | 0 | |
5 | 5 | 5 | 5 | 5 | 0 | |
6 | 0 | 0 | 0 | 0 | 0 |
[ 정답 코드 ]
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
class Main{
public static void main(String[] args)throws Exception{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int N = Integer.parseInt(st.nextToken());// 사각형크기(1<=1,000)
int M = Integer.parseInt(st.nextToken());// 질의 수(1<=300,000)
long origin[][] = new long[N + 2][N + 2];
long diff[][] = new long[N + 2][N + 2]; // 차분 배열
for(int y=1; y<=N; y++)
{
st = new StringTokenizer(br.readLine());
for(int x=1; x<=N; x++)
origin[y][x] = Integer.parseInt(st.nextToken());
}
// 구간 질의를 입력받으며 차분 배열 4점에 마킹한다.
for(int i=1; i<M; i++)
{
st = new StringTokenizer(br.readLine());
st.nextToken();
int y1 = Integer.parseInt(st.nextToken()) + 1;
int x1 = Integer.parseInt(st.nextToken()) + 1;
int y2 = Integer.parseInt(st.nextToken()) + 1;
int x2 = Integer.parseInt(st.nextToken()) + 1;
int k = Integer.parseInt(st.nextToken());
// 차분배열의 네점에 마킹
diff[y1][x1] += k;
diff[y1][x2 + 1] -= k;
diff[y2 + 1][x1] += -k;
diff[y2 + 1][x2 + 1] += k;
}
// 가로만 먼저 돌면서 다더함
for(int y=1; y<=N; y++)
for(int x=1; x<=N; x++)
diff[y][x] += diff[y][x-1];
// 세로를 돌며 다 더함
for(int x=1; x<=N; x++)
for(int y=1; y<=N; y++)
diff[y][x] += diff[y-1][x];
// 마지막 목표 값을 입력받아 출력한다.
st = new StringTokenizer(br.readLine());
st.nextToken();
int y1 = Integer.parseInt(st.nextToken()) + 1;
int x1 = Integer.parseInt(st.nextToken()) + 1;
int y2 = Integer.parseInt(st.nextToken()) + 1;
int x2 = Integer.parseInt(st.nextToken()) + 1;
long result = 0;
// 해당 위치만 돌며 값을 더함
for(int y=y1; y<=y2; y++)
for(int x=x1; x<=x2; x++)
result += origin[y][x] + diff[y][x];
System.out.print(result);
}
}
'알고리즘 > 차분 배열' 카테고리의 다른 글
BOJ 백준 12858 Range GCD (0) | 2025.06.01 |
---|