본문 바로가기

알고리즘/BOJ

[C++]백준 2630 색종이 만들기

문제 링크 : https://www.acmicpc.net/problem/2630

 

2630번: 색종이 만들기

첫째 줄에는 전체 종이의 한 변의 길이 N이 주어져 있다. N은 2, 4, 8, 16, 32, 64, 128 중 하나이다. 색종이의 각 가로줄의 정사각형칸들의 색이 윗줄부터 차례로 둘째 줄부터 마지막 줄까지 주어진다.

www.acmicpc.net

 

문제

아래 <그림 1>과 같이 여러개의 정사각형칸들로 이루어진 정사각형 모양의 종이가 주어져 있고, 각 정사각형들은 하얀색으로 칠해져 있거나 파란색으로 칠해져 있다. 주어진 종이를 일정한 규칙에 따라 잘라서 다양한 크기를 가진 정사각형 모양의 하얀색 또는 파란색 색종이를 만들려고 한다.

전체 종이의 크기가 N×N(N=2k, k는 1 이상 7 이하의 자연수) 이라면 종이를 자르는 규칙은 다음과 같다.

전체 종이가 모두 같은 색으로 칠해져 있지 않으면 가로와 세로로 중간 부분을 잘라서 <그림 2>의 I, II, III, IV와 같이 똑같은 크기의 네 개의 N/2 × N/2색종이로 나눈다. 나누어진 종이 I, II, III, IV 각각에 대해서도 앞에서와 마찬가지로 모두 같은 색으로 칠해져 있지 않으면 같은 방법으로 똑같은 크기의 네 개의 색종이로 나눈다. 이와 같은 과정을 잘라진 종이가 모두 하얀색 또는 모두 파란색으로 칠해져 있거나, 하나의 정사각형 칸이 되어 더 이상 자를 수 없을 때까지 반복한다.

위와 같은 규칙에 따라 잘랐을 때 <그림 3>은 <그림 1>의 종이를 처음 나눈 후의 상태를, <그림 4>는 두 번째 나눈 후의 상태를, <그림 5>는 최종적으로 만들어진 다양한 크기의 9장의 하얀색 색종이와 7장의 파란색 색종이를 보여주고 있다.

입력으로 주어진 종이의 한 변의 길이 N과 각 정사각형칸의 색(하얀색 또는 파란색)이 주어질 때 잘라진 하얀색 색종이와 파란색 색종이의 개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에는 전체 종이의 한 변의 길이 N이 주어져 있다. N은 2, 4, 8, 16, 32, 64, 128 중 하나이다. 색종이의 각 가로줄의 정사각형칸들의 색이 윗줄부터 차례로 둘째 줄부터 마지막 줄까지 주어진다. 하얀색으로 칠해진 칸은 0, 파란색으로 칠해진 칸은 1로 주어지며, 각 숫자 사이에는 빈칸이 하나씩 있다.

출력

첫째 줄에는 잘라진 햐얀색 색종이의 개수를 출력하고, 둘째 줄에는 파란색 색종이의 개수를 출력한다.

예제 

입력 출력
8
1 1 0 0 0 0 1 1
1 1 0 0 0 0 1 1
0 0 0 0 1 1 0 0
0 0 0 0 1 1 0 0
1 0 0 0 1 1 1 1
0 1 0 0 1 1 1 1
0 0 1 1 1 1 1 1
0 0 1 1 1 1 1 1
9
7

풀이 방법

  • 분할 정복
  • 재귀 형식으로 종이를 위아래/좌우 4 부분으로 분할해나가면서,
    ▶ 나눠진 네 부분이 모두 하나의 같은 색으로 이루어진 경우 : 1 반환
    ▶ 그렇지 않을 경우 : 0 반환 (반환 전 4 부분의 색이 각각 무엇인지에 따라 색종이의 개수를 추가함)
  • color[0] = 흰색, color[1] = 파란색

풀이 코드

// 분할 정복
// 4ms
#include <iostream>
using namespace std;

int N;
int board[128][128];
int color[2] = {0, 0};

// 네 조각의 색이 모두 같은지 확인
int isSame(int s, int e, int length) {
	if (board[s][e] == board[s + length][e] && board[s + length][e] == board[s][e + length] && board[s][e + length] == board[s + length][e + length])
		return 1;
	else
		return 0;
}
// 위아래/좌우 4개로 분할해 색이 같은지 확인
int countDivide(int s, int e, int length) {

	length /= 2;
	// basecase
	if (length < 1) return 1;

	int upL = countDivide(s, e, length);
	int upR = countDivide(s + length, e, length);
	int downL = countDivide(s, e + length, length);
	int downR = countDivide(s + length, e + length, length);
	// 각 부분들이 모두 하나의 색으로만 이루어져 있고, 그 색들이 모두 같을 때
	if (upL == 1 && upR == 1 && downL == 1 && downR == 1 && isSame(s, e, length)) return 1;
	// 그 외
	if (upL) color[board[s][e]]++;
	if (upR) color[board[s + length][e]]++;
	if (downL) color[board[s][e + length]]++;
	if (downR) color[board[s + length][e + length]]++;
	return 0;
}

int main() {
	cin >> N;
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			cin >> board[i][j];
		}
	}
	if(countDivide(0, 0, N)) color[board[0][0]]++;
	cout << color[0] << endl << color[1];
}

 

'알고리즘 > BOJ' 카테고리의 다른 글

[C++]백준 2839 설탕배달  (0) 2021.08.03
[C++]백준 2798 블랙잭  (0) 2021.07.21