[백준] 1780번 종이의 개수


Divide & Conquer

문제설명

문제

N×N크기의 행렬로 표현되는 종이가 있다. 종이의 각 칸에는 -1, 0, 1의 세 값 중 하나가 저장되어 있다. 우리는 이 행렬을 적절한 크기로 자르려고 하는데, 이때 다음의 규칙에 따라 자르려고 한다.

만약 종이가 모두 같은 수로 되어 있다면 이 종이를 그대로 사용한다.

  1. (1)이 아닌 경우에는 종이를 같은 크기의 9개의 종이로 자르고, 각각의 잘린 종이에 대해서 (1)의 과정을 반복한다.
  2. 이와 같이 종이를 잘랐을 때, -1로만 채워진 종이의 개수, 0으로만 채워진 종이의 개수, 1로만 채워진 종이의 개수를 구해내는 프로그램을 작성하시오.

입력

첫째 줄에 N(1≤N≤3^7, N은 3^k 꼴)이 주어진다. 다음 N개의 줄에는 N개의 정수로 행렬이 주어진다.

출력

첫째 줄에 -1로만 채워진 종이의 개수를, 둘째 줄에 0으로만 채워진 종이의 개수를, 셋째 줄에 1로만 채워진 종이의 개수를 출력한다.

접근

전형적인 Divide & Conquer(분할정복) 문제이다. 입력된 배열을 조건을 확인하며 아홉 부분으로 나누어 재귀적으로 해결하면 된다.

코드

#include <iostream>
#define MAX_NUM 2200
using namespace std;

int arr[MAX_NUM][MAX_NUM];
int ans[3];

bool checkArray(int a, int b, int term){
    int num = arr[a][b];
    for(int i=0; i<term; i++){
        for(int j=0; j<term; j++){
            if(num != arr[a+i][b+j])
                return false;
        }
    }
    return true;
}

void divideNconquer(int a, int b, int term){

    bool isAllSame = checkArray(a, b, term);
    if(!isAllSame){
        int new_term = term/3;
        for(int i=0; i<3; i++){
            int new_a = a + i*new_term;
            for(int j=0; j<3; j++){
                int new_b = b + j*new_term;
                divideNconquer(new_a, new_b, new_term);
            }
        }
    }
    else{
        int num = arr[a][b];
        ans[num+1]++;
    }
}

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

    int n;
    cin >> n;
    for(int i=0; i<n; i++){
        for(int j=0; j<n; j++){
            cin >> arr[i][j];
        }
    }

    divideNconquer(0,0,n);

    for(auto x : ans)
        cout << x << '\n';
}

참고

BOJ #1780

이 글이 도움이 되셨다면 추천 클릭을 부탁드립니다 :)




© 2021. by Gyeong-Hyeon Kim

Powered by ddamddi