백준 11660 - 구간 합 구하기5

2 minute read

문제링크

문제 접근

  • 그림과 같이 2차원 배열의 부분 면적에 속한 원소들의 합을 구하는 문제라고 직관적으로 이해하면 쉽다.
  • 반복문을 중첩하여 해당 면적의 원소들을 일일이 더해가는 방법도 있겠지만 시간초과에 무조건 걸릴 것으로 판단했다. 또, 문제에서 의도하는 풀이방법은 아닐 것이다.
  • 부분합을 구할 때 가장 많이 사용하는 방법은 a(n) = S(n) - S(n-1)의 원리를 이용하는 방법이다.
  • 예를 들어, 3부터 10까지의 합을 구할 때 우리는 1부터 10까지의 합은 55라는 것을 통상적으로 알고 있으므로 55 - (1부터 2까지의 합) = (3부터 10까지의 합) 이 됨을 판단할 수 있다.
  • 이 원리를 dp 알고리즘으로 접근하여 문제를 해결하였다.

의사코드(pseudo-code)

- input N, M
- init vector<vector<int>> prefix(N+1, vector<int>(N+1, 0))
- init sum = 0
- for i=1 to N do:
	- for j=1 to N do:
		- input an integer in a varaible, n
		- sum += n
		- prefix[i][j] = sum
	- if i>1 then:
		- prefix[i][0] = prefix[i-1][N]
- for k=0 to M-1 do:
	- init result=0
	- input x1,y1,x2,y2
	- for i=x1 to x2 do:
		- result += prefix[i][y2] - prefix[i][y2-1]
	- print "result"

소스코드 및 분석

전체 코드

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	int N, M;
	scanf("%d %d", &N, &M);
	vector<vector<int>> prefix(N + 1, vector<int>(N + 1, 0));
	int sum = 0;
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= N; j++) {
			int n;
			scanf("%d", &n);
			sum += n;
			prefix[i][j] = sum;
		}
		if (i > 1) prefix[i][0] = prefix[i - 1][N];
	}

	for (int k = 0; k < M; k++) {
		int x1, y1, x2, y2;
		int result = 0;
		scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
		for (int i = x1; i <= x2; i++) {
			result += prefix[i][y2] - prefix[i][y1- 1];
		}
		printf("%d\n", result);
	}
}

부분 코드

vector<vector<int>> prefix(N + 1, vector<int>(N + 1, 0));
  • 정적 메모리를 할당할 경우 스택 메모리의 초과로 프로그램이 정상적으로 실행되지 않는다. 따라서 벡터 컨테이너를 활용해 2차원 동적 배열을 형성한다.
  • 문제에서 행렬의 시작을 1행 1열로 정했기 때문에, 2차원 배열의 모든 원소를 0으로 초기화하고 N+1 만큼의 메모리를 할당했다.
int sum = 0;
for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= N; j++) {
			int n;
			scanf("%d", &n);
			sum += n;
			prefix[i][j] = sum;
		}
		if (i > 1) prefix[i][0] = prefix[i - 1][N];
	}
  • 2차원 배열의 원소들을 입력받으면서 해당 원소까지 부분합을 구하는 코드이다.
  • prefix[i][j]에는 1행 1열부터 i행 j열까지의 원소들의 합이 대입된다.
  • 주의할 점은, 만약 (x1,y1)=(1,1), (x2,y2)=(4,4) 인 경우처럼 부분합을 구할 때 열(column)에 해당하는 인덱스가 0을 가리키는 경우가 생길 수 있다. 우리는 초기화 단계에서 벡터의 모든 원소를 0으로 초기화하였기 때문에 2행 0열의 값은 1행 4열의 값이 아니라 0을 가진다.
  • 따라서 2행부터 0열의 원소는 이전 행의 마지막 원소값을 대입해주어야 부분합 계산 과정에서 문제가 발생하지 않는다.
for (int k = 0; k < M; k++) {
		int x1, y1, x2, y2;
		int result = 0;
		scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
		for (int i = x1; i <= x2; i++) {
			result += prefix[i][y2] - prefix[i][y1- 1];
		}
		printf("%d\n", result);
	}
  • 위에서 언급했던 부분합 구하는 방법을 코드로 구현하였다.

Leave a comment