어떤 배열 arr에 대해 prefix sum 배열을 만들어 배열의 index i부터 index j까지의 합을 O(1)으로 풀 수 있는 알고리즘. 배열의 크기가 O(nk)일 때 초기화는 O(nk), 구간 합을 구할 때는 O(1)의 시간이 걸린다.
특정 구간의 합을 구하는 것이기 때문에 해당 구간의 값이 변화하지 않는 것을 통해, 특정 구간 안에 원소가 있는지 없는지도 쉽게 계산할 수 있다.(2차원도 적용 가능)
range sum을 구할 때 index를 주의하도록 하자.
1. 1차원 Prefix Sum : 초기화 O(n), 쿼리당 O(1)
range sum을 구할 때, start가 0이면 -1로 out of bound가 되어버리므로 start가 0일 때만 예외 처리를 해 주자.
// return partial sum // psum[i] : sum from index 0 to index i vector<int> setPsum(vector<int>& arr){ vector<int> psum(arr.size()); psum[0] = arr[0]; for(int i = 1; i<arr.size(); i++){ psum[i] = psum[i-1] + arr[i]; } return psum; } // return range sum from index 'start' to index 'end' int getRangeSum(vector<int>& psum, int start, int end){ if(start == 0) return psum[end]; return psum[end] - psum[start - 1]; } int main(void) { ... vector<int> psum = setPsum(arr); int start, end; cin>>start>>end; cout<<getRangeSum(psum, start-1, end-1)<<'\n'; ... return 0; }
2. 2차원 Prefix Sum : 초기화 O(n2), 쿼리당 O(1)

위 그림에서 좌상단을 (0, 0), 가운데를 (r1, c1), 우하단을 (r2, c2)라고 하면, (r1, c1)부터 (r2, c2)까지의 합은 [(전체) - (파랑 + 노랑) - (초록 + 노랑) + 노랑] 이다. 이를 수식으로 표현하자면
(r1, c1)부터 (r2, c2)까지 합 = psum[r2][c2] - psum[r1-1][c2] - psum[r2][c1 - 1] + psum[r1 - 1][c1 - 1]
만약 r1이 0이면 r1이 들어가는 항은 0, c1이 0이면 c1이 들어가는 항은 0이 된다. 그러나 index 0에 0을 추가하는 간단한 트릭을 이용해 초기화에 대한 코드 작성을 조금 편리하게 할 수 있다.
초기화도 같은 맥락으로 psum[r][c] = value[r][c] + psum[r-1][c] + psum[r][c-1] - psum[r-1][c-1]로 하면 된다.
// get partial sum from point (r1, c1) to point (r2, c2) int getRangeSum(vector<vector<int>>& psum, int r1, int c1, int r2, int c2){ int result = psum[r2][c2]; if(r1 != 0) result -= psum[r1 - 1][c2]; if(c1 != 0) result -= psum[r2][c1 - 1]; if(r1 != 0 && c1 != 0) result += psum[r1 - 1][c1 - 1]; return result; } int main(void) { ... int N, M; cin>>N>>M; vector<vector<int>> board(N, vector<int>(N)), psum(N+1, vector<int>(N+1, 0)); // input for(int i = 0; i<N; i++){ for(int j = 0; j<N; j++){ cin>>board[i][j]; } } // initialize prefix sum // psum[r][c] : sum from (0, 0) to (r, c) for(int r = 1; r<=N; r++){ for(int c = 1; c<=N; c++){ psum[r][c] = board[r-1][c-1] + psum[r-1][c] + psum[r][c-1] - psum[r-1][c-1]; } } int x1, y1, x2, y2; while(M--){ cin>>x1>>y1>>x2>>y2; cout<<getRangeSum(psum, x1, y1, x2, y2)<<'\n'; } return 0; }
'PS > Algorithm' 카테고리의 다른 글
저수지 샘플링 Reservoir Sampling (0) | 2023.03.10 |
---|---|
Two Pointer, Sliding Window, Meet in the Middle ***TODO (0) | 2022.08.22 |
동적 계획법 Dynamic Programming (+ LIS) (0) | 2022.07.09 |
좌표 압축 (0) | 2022.06.28 |
정렬 ***TODO (0) | 2022.06.27 |