[백준] 20543 폭탄 던지는 태영이 (C++)

Yookyubin·2023년 7월 14일
0

백준

목록 보기
40/68

문제

20543번: 폭탄 던지는 태영이

풀이

폭탄의 범위때문에 특정 위치의 폭탄 개수를 구하기가 쉽지 않다.
하지만 태영이가 폭발 범위가 n을 넘어가게 던지지 않기 때문에 (0, 0), (0, n-1), (n-1, 0), (n-1, n-1)위치에 영향을 주는 폭탄 위치는 한 곳 밖에 없고, 따라서 각각의 해발고도는 바로 폭탄의 수라는 것을 알 수 있다.
만약 (0, 0)위치의 해발고도로 (m/2, m/2)위치의 폭탄 개수를 구했다면 (0, 1) 해발고도에 영향을 준 폭탄의 위치가 (m/2, m/2), (m/2, m/2+1) 2개이기 때문에 해발고도(0, 1) - 폭탄개수(m/2, m/2) = 폭탄개수(m/2, m/2+1)으로 구할 수 있다.
이처럼 어떤 위치의 해발고도에 영향을 준 폭탄의 개수를 하나씩 알아내며 모든 위치의 폭탄을 알아내야한다. 한 위치의 해발고도에 영향을 준 폭탄의 개수를 이미 알고 있는 폭탄의 개수를 이용하여 지우다보면 특정 폭탄위치 하나만 남게되며 그 위치의 폭탄 개수를 구할 수 있다.
매번 알고 있는 위치의 폭탄의 영향을 지웠을때 남은 폭탄의 위치가 하나일 수 있도록 왼쪽 위부터 한줄씩 n-m/2만큼씩 n-m/2줄을 탐색한다.

하지만 매번 한 위치의 해발고도에 영향을 준 폭탄의 개수를 더한다면 시간초과가 발생한다. 이를 위해 누적합을 사용한다.
폭탄의 개수를 구하는 반복문안에서 폭탄의 개수를 구하며 누적합도 함께 구한다.
폭탄의 개수를 구했다면 imos법으로 그 폭탄의 값이 앞으로 구할 누적합에 영향을 줄 수 있도록 배열 갱신도 함께한다. imos참고: 파괴되지 않은 건물

폭탄을 구하는 방법은 해발고도에서 2차원 부분 누적합를 이용해 구할 수 있다.
해발고도[i][j] = 누적합[i-1][j] + 누적합[i][j-1] - 누적합[i-1][j-1] + 원소값[i][j]이다.
이때 원소값[i][j]는 imos법으로 갱신한 값과 폭탄 개수의 합이된다.
따라서 원소값[i][j] = 누적합[i][j] + 폭탄수[i][j]가 된다. 이때 주의해야 할 것은 누적합[i][j]의 값이 이미 완성된 값이 아닌 imos에 의해 갱신된 값일 뿐이라는 것을 알아야 한다.
식을 폭탄수에 대해 정리하면
폭탄수[i][j] = 해발고도[i][j] - 누적합[i-1][j] - 누적합[i][j-1] + 누적합[i-1][j-1] - 누적합[i][j]이 된다.
식에서도 알 수 있듯이 사실 해발고도[i][j]는 완성된 누적합이다.

정리하면 알고 있는 누적합과 그 누적합을 구하는 과정을 이용하여 특정 위치의 원소값을 구하는 과정이된다.

누적합을 구하고 갱신하는 과정에서 주어진 배열 밖의 인덱스를 참조하는 경우가 생기는데 이때의 예외처리를 잘 해야 한다.

또한 문제의 조건으로 주어진 값이 -2,147,483,648 ≤ H[r][c] ≤ 0이다. 이는 한 위치에 폭탄이 최대 2,147,483,648개를 가질 수 있다는 뜻인데 아쉽게도 int 형에서는 최대 2,147,483,647까지만 가능하며 폭탄의 개수를 담을 변수는 64비트 크기의 자료형이어야 한다.

코드

#include <iostream>
#include <vector>

using namespace std;

int n, m;
vector<vector<int>> h;
vector<vector<int64_t>> bombs;
vector<vector<int>> prefixSum;

bool OOB(int i, int j){
    return i < 0 || i >= n || j < 0 || j >=n;
}

void addPrefixSum(int i, int j, int64_t num){
    if (!OOB(i, j)){
        prefixSum[i][j] += num;
    }
}

int64_t getPrefixSum(int i, int j){
    if (!OOB(i, j)){
        return prefixSum[i][j];
    }
    else {
        return 0;
    }

}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);

    cin >> n >> m;
    h.assign(n, vector<int>(n));
    bombs.assign(n, vector<int64_t>(n));
    prefixSum.assign(n, vector<int>(n));

    for (int i=0; i<n; i++){
        for (int j=0; j<n; j++){
            cin >> h[i][j];
        }
    }
    
    for (int i=0; i < n-(m)/2; i++){
        for(int j=0; j < n-(m)/2; j++){
            int curPrefix = getPrefixSum(i-1, j) + getPrefixSum(i, j-1) + getPrefixSum(i, j) - getPrefixSum(i-1, j-1);
            int64_t bomb = h[i][j] - curPrefix;
            prefixSum[i][j] = h[i][j]; // bomb + curPrefix
            addPrefixSum(i+m, j, -bomb);
            addPrefixSum(i, j+m, -bomb);
            addPrefixSum(i+m, j+m, bomb);
            
            bombs[i+m/2][j+m/2] -= bomb;
        }
    }

    for (int i=0; i<n; i++){
        for (int j=0; j<n; j++){
            cout << bombs[i][j] << " ";
        }
        cout << endl;
    }

    return 0;
}
profile
붉은다리 제프

0개의 댓글