// 파리퇴치 //
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class D2_2001 {
// 원본 배열
static int[][] map;
// 누적합을 저장하기 위한 배열
static int[][] sumMap;
static int T,N,M;
static int solution(int[][] map,int m) {
// 누적합을 저장해 놓기 위한 배열에 저장
for(int i=1; i<=N; i++) {
for(int j=1; j<=N; j++) {
sumMap[i][j] = map[i-1][j-1] + sumMap[i-1][j] + sumMap[i][j-1] - sumMap[i-1][j-1];
}
}
int maxSum = Integer.MIN_VALUE;
for(int i=M; i<=N; i++) {
for(int j=M; j<=N; j++) {
int sum = sumMap[i][j] - sumMap[i][j-M] - sumMap[i-M][j] + sumMap[i-M][j-M];
maxSum = Math.max(sum,maxSum);
}
}
return maxSum;
}
public static void main(String[] args) throws IOException {
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
// 테스트 케이스
T = Integer.parseInt(bf.readLine());
// #1 부터 시작함
int testCase = 1;
while (testCase <= T) {
StringTokenizer st = new StringTokenizer(bf.readLine());
// n x n 배열
N = Integer.parseInt(st.nextToken());
// m x m 배열 => 파리를 잡을 배열
M = Integer.parseInt(st.nextToken());
map = new int[N][N];
// 누적합 배열
sumMap = new int[N + 1][N + 1];
for (int i = 0; i < N; i++) {
st = new StringTokenizer(bf.readLine());
for (int j = 0; j < N; j++) {
map[i][j] = Integer.parseInt(st.nextToken());
}
}
System.out.println("#"+testCase+ " "+ solution(map, M));
testCase ++;
}
} // while 문 종료
}
<입력>
2
5 2
1 3 3 6 7
8 13 9 12 8
4 16 11 12 6
2 4 1 23 2
9 13 4 7 3
6 3
29 21 26 9 5 8
21 19 8 0 21 19
9 24 2 11 4 24
19 29 1 0 21 19
10 29 6 18 4 3
29 11 15 3 3 29
<출력>
#1 49
#2 159
예를 들어 4 x 4 크기의 Map 배열이 있고, 누적합을 구하기 위한 5 x 5 크기의 sumMap 배열이 있다고 하자.
// 누적합을 저장해 놓기 위한 배열에 저장
for(int i=1; i<=N; i++) {
for(int j=1; j<=N; j++) {
sumMap[i][j] = map[i-1][j-1] + sumMap[i-1][j] + sumMap[i][j-1] - sumMap[i-1][j-1];
}
}
인덱스 번호는 1부터 시작한다. 그림과 같이 sum[1][1] 부터 저장을 하는데,
sumMap[i][j] = Map[i-1][j-1] + sumMap[i-1][j] + sumMap[i][j-1] - sumMap[i-1][j-1];
만약 Map[1][1] "까지" 의 2 x 2 크기 누적합(1+2+2+3 = 8) 을 구하고 싶다면,
Map[1][1] + sumMap[1][2] + sumMap[2][1] - sumMap[1][1] ,
즉 3 + 3 + 3 - 1 = 8 와 같은 방법으로 누적합을 저장한다.
for(int i=M; i<=N; i++) {
for(int j=M; j<=N; j++) {
int sum = sumMap[i][j] - sumMap[i][j-M] - sumMap[i-M][j] + sumMap[i-M][j-M];
maxSum = Math.max(sum,maxSum);
}
}
반복문의 시작은 M이다.
이 부분은 그냥 공식처럼 암기하기로 했다.
그림에서와 같이
이 계산은 특별한 것이 아니라 그냥 영역의 합을 구하고 싶을 때 전체 영역에서 더하고 빼고 하는 방식을 적용한 것이다.
구한 합들을 변수에 저장해 놓고 Math.max 함수로 최대합을 구한다.