포스트

(백준/C++) 1780_종이의 개수

1780번: 종이의 개수 문제는 분할 정복(Divide and Conquer) 문제입니다.

분할 정복은 큰 문제를 작은 부분 문제로 나누어 해결하고, 그 결과를 합쳐 원래 문제의 해결책을 찾는 방법입니다.

만약 이전에 1992번: 쿼드트리 (acmicpc.net) 문제나 2630번: 색종이 만들기 (acmicpc.net) 문제를 풀어보셨다면 쉽게 푸실 수 있는 문제입니다.


접근법

분할 정복(Divide and Conquer)은 세 가지 접근법으로 먼저 생각해볼 수 있습니다.

바로, 분할 - 정복 - 결합 입니다.

1) 분할(Divide)

현재 영역의 데이터가 모두 같은 값인지 확인합니다.

만약 모두 같은 값이 아니라면, $N×N$ 사이즈의 종이를 $\frac{N}{3}×\frac{N}{3}$으로 나눕니다.

2) 정복(Conquer)

만약 나눠진 종이가 모두 같은 숫자(-1, 0, 1)로 채워져 있다면, 해당 숫자의 카운트를 증가시킵니다. 그렇지 않다면, 분할 단계를 반복합니다.

기저 사례(Base Case): 종이의 크기가 $1×1$이 되면, 해당 칸의 숫자(-1, 0, 1)의 카운트를 증가시킵니다

3) 결합(Combine)

정복 단계의 결과를 합쳐 최종 결과를 출력하는 단계입니다.

이 문제에서는 전역 변수로 해당 숫자를 증가 시켰다면, 따로 결합 단계는 존재하지 않습니다. 하지만, 리턴 값과 같은 방식으로 설계했다면, 해당 값들을 결합해야 합니다.


풀이

제 풀이에서는 기저 사례를 size를 줄여가며 호출하기 때문에, for문에서 걸러집니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iostream>
using namespace std;

int paper[2187][2187];
int numCnt[3];

void solve(int x, int y, int size)
{
	//데이터를 확인한다.
	int check = paper[x][y];
	for (int i = x; i < x + size; i++)
	{
		for (int j = y; j < y + size; j++)
		{
			if (check != paper[i][j])
			{
				//데이터가 서로 다르면 한 번 더 9칸으로 잘라야 한다.
				int nextSize = size / 3;
				for(int _x = 0; _x < 3; _x++)
				{
					for(int _y = 0; _y < 3; _y++)
					{
						solve(x + _x * nextSize, y + _y * nextSize, nextSize);
					}
				}
				return;
			}
		}
	}

	numCnt[check + 1]++;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(NULL); cout.tie(NULL);

	int N;
	cin >> N;

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

	solve(0, 0, N);

	for(const int& n : numCnt)
		cout << n << '\n';
}
이 기사는 저작권자의 CC BY-NC-ND 4.0 라이센스를 따릅니다.