[ 문제 ] : https://www.acmicpc.net/problem/1780
1780번: 종이의 개수
N×N크기의 행렬로 표현되는 종이가 있다. 종이의 각 칸에는 -1, 0, 1 중 하나가 저장되어 있다. 우리는 이 행렬을 다음과 같은 규칙에 따라 적절한 크기로 자르려고 한다. 만약 종이가 모두 같은 수
www.acmicpc.net
[ 문제 접근 ]
문제를 보면 분할 정복인건 알겠고, 재귀를 이용하면 되는거 까지는 쉽게 알 수 있었는데.
코드로 직접 짜보는거는 쉽지 않았다.
우선 입력으로 주어진 배열을 받을 전역변수 arr 를 만들어줬고,
재귀를 사용 해야하기에 base case 가 뭘지 생각해줬다.
색종이가 1개의 원소를 가리킬때가 base case 라는걸 알 수 있었고, 그러기 위해서는 각 단계마다 n은 3배씩 줄어 들어야 함을 알아냈다.
이제 재귀함수의 인자로 무엇을 넘겨줄까를 고민해 보았는데, divide 할때마다 행 과 열이 1/3 씩으로 나뉘기에
0 , 3 , 6 처럼 행과 열의 좌표를 3씩 늘어나게끔 해서 총 9번의 재귀함수를 호출했다.
[ 최종 코드 ]
#include<iostream>
#include<vector>
#include<string>
using std::cin; using std::cout;
using std::vector;
int n;
int arr[2188][2188];
int result[3];
void solve(int n,int a,int b){
if (n == 1) {
result[arr[a][b] + 1]++;//인덱스는 음수 일 수 없기에 전부 +1씩 해준 다음에 배열에 저장.
return;
}
bool is_same = true;
for(int i=a;i<a+n;i++){
for (int j = b; j < b+n; j++) {
if (arr[a][b] != arr[i][j]) {
is_same = false;
break;
}
}
if (!is_same)break;
}
if (is_same) {
result[arr[a][b] + 1]++;
return;
}
else {
int temp = n / 3;
solve(n / 3, a, b);
solve(n / 3, a, b+temp);
solve(n / 3, a, b + 2 * temp);
solve(n / 3, a + temp, b);
solve(n / 3, a + temp, b + temp);
solve(n / 3, a + temp, b + 2 * temp);
solve(n / 3, a + 2 * temp, b);
solve(n / 3, a + 2 * temp, b + temp);
solve(n / 3, a + 2 * temp, b + 2 * temp);
}
}
int main() {
std::ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
cin >> n;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> arr[i][j];
}
}
solve(n,0,0);
cout << result[0] << '\n' << result[1] << '\n' << result[2];
return 0;
}
[ 다른 사람 코드 ]
👉 색종이가 같은 원소라만 이루어져 있는지 확인하는 함수를 따로 만들어줘도 된다.
int N;
int paper[2200][2200];
int cnt[3]; //-1, 0, 1로 채워진 종이 갯수
//해당 종이 내부에 같은 숫자로만 채워졌는지 확인하는 함수
bool check(int x, int y, int n) {
for (int i = x; i < x + n; i++)
for (int j = y; j < y + n; j++)
if (paper[x][y] != paper[i][j])
return false;
return true;
}
void solve(int x, int y, int z)
{
if (check(x, y, z)) {
cnt[paper[x][y] + 1] += 1;
return;
}
int n = z / 3;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
solve(x + i * n, y + j * n, n);
}
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0);
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 (int i = 0; i < 3; i++) cout << cnt[i] << "\n";
}
👉 색종이가 같은 숫자인지 아닌지 판단은 여러가지 방법으로 할 수 있다.
#include<bits/stdc++.h>
#define MAX 2333
using namespace std;
int arr[MAX][MAX];
int cnta=0, cntb=0, cntc=0;
int reta=0, retb=0, retc=0;
void f(int y, int x, int size) {
cnta=0, cntb=0, cntc=0;
for(int i=y; i<y+size; i++) {
for(int j=x; j<x+size; j++) {
if(arr[i][j]==-1) cnta++;
else if(arr[i][j]==0) cntb++;
else if(arr[i][j]==1) cntc++;
}
}
if(cnta==size*size) reta++;
else if(cntb==size*size) retb++;
else if(cntc==size*size) retc++;
else {
f(y, x, size/3); // 왼쪽맨위처음
f(y, x+size/3, size/3); // 왼쪽맨위중간
f(y, x+size/3*2, size/3); // 왼쪽맨위마지막
f(y+size/3, x, size/3); // 중간처음
f(y+size/3, x+size/3, size/3); // 중간중간
f(y+size/3, x+size/3*2, size/3); // 중간마지막
f(y+size/3*2, x, size/3); // 마지막처음
f(y+size/3*2, x+size/3, size/3); // 마지막중간
f(y+size/3*2, x+size/3*2, size/3); // 마지막마지막
}
return;
}
int main() {
int n;
cin >> n;
for(int i=0; i<n; i++) {
for(int j=0; j<n; j++) {
cin >> arr[i][j];
}
}
f(0, 0, n);
cout << reta << endl << retb << endl << retc;
return 0;
}
[ Key Point ]
👉 base case 에는 반드시 return 을 써주자.
[ 다른 사람 풀이 ]
ref :: https://jaehoon0124welcome.tistory.com/m/20?category=940919
'PS > BaekJoon' 카테고리의 다른 글
[2252] 쿼드트리 - 재귀/분할정복 ★★☆☆ / 복습 ○ (0) | 2021.12.02 |
---|---|
[2630] 색종이 만들기 - 재귀, 분할정복 ★★☆☆ / 복습 ○ (0) | 2021.12.02 |
[10799] 쇠막대기 - 스택 ★★☆☆ / 복습 ○ (0) | 2021.11.30 |
[1504] 특정한 최단 경로 - 최단경로 ★★★☆ / 복습 ○ (0) | 2021.11.28 |
[5430] AC - 구현/문자열/덱 ★★☆☆ / 복습 ○ (0) | 2021.11.26 |