[ 문제 ] : 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 |