알고리듬/문제

[baekjoon] 14846

narmeee 2023. 5. 22. 01:26

직사각형과 쿼리

문제

N행 N열로 이루어진 정사각형 행렬 A가 주어진다. 이때, 쿼리를 수행하는 프로그램을 작성하시오.

  • x1 y1 x2 y2: 왼쪽 윗칸이 (x1, y1)이고, 오른쪽 아랫칸이 (x2, y2)인 부분 행렬에 포함되어 있는 서로 다른 정수의 개수를 출력한다.

입력

첫째 줄에 N (1 ≤ N ≤ 300)이 주어진다. 다음 N개의 줄에는 행렬의 정보가 주어지며, 각 줄은 N개의 수로 이루어져 있다. 행은 위에서부터 아래로, 열은 왼쪽부터 오른쪽으로 번호가 매겨져 있으며, 번호는 1번부터 시작한다. 행렬이 포함하고 있는 정수는 10보다 작거나 같은 자연수이다.

다음 줄에는 Q (1 ≤ Q ≤ 100,000)가 주어진다. 다음 Q개의 줄에는 쿼리의 정보 x1, y1, x2, y2가 주어진다. (1 ≤ x1 ≤ x2 ≤ n, 1 ≤ y1 ≤ y2 ≤ n)

출력

각각의 쿼리마다 정답을 한 줄에 하나씩 출력한다.

예제 입력

3
1 2 3
3 2 1
5 6 3
3
1 1 2 3
2 2 2 2
1 1 3 3

예제 출력

3
1
5

풀이

쿼리가 최대 10만개 이기 때문에 매번 탐색을 돌리기엔 시간이 많이 소요된다.

행렬이 포함하고 있는 정수의 범위가 1  K  10 이고, 1  ≤ N ≤ 300 이기 때문에 충분히 DP를 구성할 수 있다.

DP [K][X][Y]의 의미는 XY에 포함된 K의 개수이다.DP [X][Y] = DP [X-1][Y] + DP [X][Y-1] - DP [X-1][Y-1] 이 된다.

1에 대한 DP

다음으로 쿼리 x1 y1 x2 y2 에 대한 값을 구해야 한다.

예를 들어 쿼리 2,2,3,3 일 때 부분 행렬에 포함된 1의 개수 = DP [1][3][3] - DP [1][3][1] - DP [1][1][3] + DP [1][1][1]이다.

파란 영역을 빼주는 과정에서 겹치는 부분이 발생하기 때문에 마지막에 겹친 부부를 더 해준다.

이걸 식으로 바꿔보면  dp [number][x2][y2] - dp [number][x2][y1-1] - dp [number][x1-1][y2] + dp [number][x1-1][y1-1]이다.

식의 값이 0보다 크다면 개수를 하나 더 해주고 이 과정을 최대 정수 K까지 반복한다.

 

 

#include <iostream>
#include <vector>
#define MAXSIZE 300
using namespace std;

int board[MAXSIZE+1][MAXSIZE+1];
int dp[11][MAXSIZE+1][MAXSIZE+1];
int n, q, maxn;

int calc(int n, int x, int y){    
    int dx[3] = {-1, 0, -1};
    int dy[3] = {0, -1, -1};
    int result = 0;
    for(int i = 0; i < 2; i++){
        int nx = x + dx[i];
        int ny = y + dy[i];
        result += dp[n][ny][nx];
    }
    result -= dp[n][y+dy[2]][x+dx[2]];
    return result;
}

void input(){
    cin >> n;
    
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= n; j++){
            cin >> board[i][j];
            maxn = max(board[i][j], maxn);
        }
    }
}

void solve(){
    //set dp
    for(int i = 1; i <= maxn; i++){
        for(int y = 1; y <= n; y++){
            for(int x = 1; x <= n; x++){
                dp[i][y][x] = calc(i, x, y);
                dp[i][y][x] += board[y][x] == i;
            }
        }
    }
    int x1, y1, x2, y2;
    cin >> q;
    vector<int> answer;
    for(int i = 0; i < q; i++){
        cin >> x1 >> y1 >> x2 >> y2;
        int sum = 0;
        for(int number = 1; number <= maxn; number++){
            sum += (dp[number][x2][y2] - dp[number][x2][y1-1] - dp[number][x1-1][y2] + dp[number][x1-1][y1-1]) > 0;
        }
        answer.push_back(sum);
    }

    for(auto i : answer){
        cout << i << "\n";
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    input();
    solve();
}

 

'알고리듬 > 문제' 카테고리의 다른 글

[baekjoon] 23290  (0) 2023.05.28
[baekjoon] 17135  (0) 2023.05.23
[baekjoon] 1976  (1) 2023.05.21
[baekjoon] 10021  (0) 2023.05.20
[baekjoon] 15591  (0) 2023.05.19