Skip to content

Conversation

@dalsu0222
Copy link
Collaborator

@dalsu0222 dalsu0222 commented Aug 11, 2024

🔗 문제 링크

[Silver 2] 백준 2630번 색종이 만들기 https://www.acmicpc.net/problem/2630
n x n 크기의 색종이가 주어진다. 각 정사각형들은 하얀색으로 칠해져 있거나 파란색으로 칠해져 있다. 주어진 종이를 일정한 규칙에 따라 잘라서 다양한 크기를 가진 정사각형 모양의 하얀색 또는 파란색 색종이를 만들려고 한다.
image
이를 자르는 규칙은 다음과 같다.
전체 종이가 모두 같은 색으로 칠해져 있지 않으면 가로와 세로로 중간 부분을 잘라서 <그림 2>의 I, II, III, IV와 같이 똑같은 크기의 네 개의 N/2 × N/2색종이로 나눈다. 나누어진 종이 I, II, III, IV 각각에 대해서도 앞에서와 마찬가지로 모두 같은 색으로 칠해져 있지 않으면 같은 방법으로 똑같은 크기의 네 개의 색종이로 나눈다. 이와 같은 과정을 잘라진 종이가 모두 하얀색 또는 모두 파란색으로 칠해져 있거나, 하나의 정사각형 칸이 되어 더 이상 자를 수 없을 때까지 반복한다.

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

예제 입력 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
예제 출력 1
9
7

💡 풀이 아이디어

☑️처음 접근한 방법

  • 하나의 색으로 이루어졌는지 확인 → 아니라면 4등분으로 쪼개기(Ⅰ,Ⅱ,Ⅲ,Ⅳ 구역) → 하나의 색으로 이루어졌는지 확인 → 아니라면4등분으로 쪼개기 ..... 일련의 과정이 반복되므로, 이를 함수화시키면 좋을 것이라고 생각했습니다.
  • 하나의 정사각형 칸이 되어 더 이상 자를 수 없을 때까지(변의 길이가 1이 될때까지) 계속 반복해야하므로, 이를 재귀함수화 한다음에 인자로 한 변의 길이를 넘겨주어서 한 변의 길이로 하는 정사각형의 넓이에 해당하는 색깔을 전부 완전탐색으로 체크하면 될 것이라고 생각했습니다.
  • 각 정사각형의 영역을 전부 체크하기 위해 시작 지점을 정사각형의 왼쪽 위 꼭짓점이라고 가정하고 문제를 바라보았습니다.
    4등분할때마다 4개의 새로운 정사각형이 새로 계속 생겨나고, 각각의 4등분된 정사각형은 길이는 전부 (자르기 전 정사각형 길이)/4 로 동일할테지만 , 4등분된 정사각형은 전부 시작지점이 달라질 것입니다.(왼쪽 위 꼭짓점이 전부 달라짐)
  • 그래서 재귀함수의 인자로 정사각형의 시작지점 (i,j) 또한 인자로 넘겨주는 것으로 설계했습니다.

💬 풀이 과정

☑️사용한 변수

  • int n : 초기 정사각형 길이
  • int paper[130][130] : 입력받는 n x n 색종이를 저장할 변수. 1 <= n <= 128이므로, 크기는 여유롭게 [130][130]으로 선언해주었습니다.
  • int white, blue : 결과로 출력할 잘라진 햐얀색 색종이의 개수, 파란색 색종이의 개수에 해당합니다.

☑️풀이

void countPaper(int x, int y, int side) 함수

  • 함수의 인자 : (x : 시작지점 i, y : 시작지점 j, side : 한 변의 길이)
  • bool isAllSame = true : 탐색하고자 하는 구역이 전부 색상이 동일한지 저장하는 상태 변수 입니다. true로 설정하여, 추후 다른 값이 발견될경우, false로 업데이트할 예정입니다.
  • int color = paper[x][y] : 시작지점 (x,y)에 해당하는 색깔을 초기 색상 값으로 설정하고, 나머지 값들이 이와 같은지 체크합니다.
  • 2차 반복문을 사용해서 바깥반복문에서는 x ~ x+side 만큼, 내부 반복문에서는 y ~ y+side 만큼 색종이 배열 paper를 전부 탐색하였습니다. 탐색과정에서 color와 다른 색깔이 등장한다면, isAllSame bool 변수를 false로 업데이트하고 실행시간을 조금이라도 줄이기 위해 바로 반복문을 빠져나왔습니다.
  • 2차 반복문이 전부 끝나면, isAllSame 값에 따라 색종이를 더 자를지 말지 판단합니다. true이면 전부 동일한 색상이어서 더 자를 필요가 없으므로, 해당 색상의 색종이 개수를 1 증가시켜주고 함수를 종료합니다.
  • false이면 더 잘라야 하므로, 4등분하여 색상을 다시 확인합니다. 4등분한만큼 재귀함수를 4번 사용하여 각 4등분하는 색종이의 시작지점을 달리하여 색상을 체크해줍니다.
  • 재귀함수는 무한루프에 빠지지 않기위해 base case를 잘 설정해주어야 하는데, countPaper() 함수의 경우 rside = 1일때는 무조건 isAllSame = true가 되므로 무한루프에 빠지지 않게 됩니다.

😥 겪었던 어려움

함수 인자의 설정

반복되는 과정을 함수화해야되는 것까지는 접근하였는데, 함수화 과정에서 어떤 인자가 필요한지 결론을 도출해내는데에 확신이 서지 않아 많은 시간을 할애하게 된 것 같습니다.

실행 시간 1초의 고려

실행시간을 1초로 해결해야하는데, 재귀함수에서 2차 반복문을 쓴다는 것이 꽤나 큰 리스크로 다가왔고 시간 초과가 뜨는것은 아닌가 걱정했습니다. 다행히 2차 반복문 안에서 다른 색상이 발견될때 바로 상태변수를 업데이트하고 반복문을 빠져나왔더니, 잘 해결된것 같습니다.
하지만 체스판처럼 이웃한 색종이 색이 전부 다른 최악의 경우 O(n*n)의 실행시간이 생길수도 있을거 같긴한데 (의문?) 다행히 이번 문제에서는 n의 최댓값이 128으로 그렇게 크지 않아 가능했던 것 같습니다.

📚 참고 자료

이 문제에 대한 다른 풀이에 대해 찾아보니, 색종이 자르기 문제가 분할 정복 문제였습니다.
분할 정복 알고리즘 (분할정복 알고리즘에 관한 글을 찾아 첨부하였습니다. 궁금하신 분들은 읽어보세요!!)
분할정복 알고리즘은 그대로 해결할 수 없는 문제를 작은 문제로 분할하여 문제를 해결하는 방법인데, 재귀알고리즘이 가장 많이 쓰이지만 일반 재귀함수와 다른점은 나눠지는 데이터의 크기가 거의 일정하다는 것입니다.
또한 분할 정복의 알고리즘들은 O(nlogn)의 실행 시간을 가져서, 위에서 최악의 실행경우에 O(N*N)일것 같다고 했는데 분할 정복의 문제인 경우 **O(nlogn)**이 됨을 처음 알게 되었습니다.

분할 정복이라는 새로운 알고리즘을 ... 알게되어 기쁘기도 하지만 아직 가야할 길이 멀었음을 느꼈습니다 😓

@dalsu0222 dalsu0222 added 💻 BOJ 백준 온라인 저지의 문제 ✨ CPP C++로 푼 문제 labels Aug 11, 2024
@dalsu0222 dalsu0222 self-assigned this Aug 11, 2024
Copy link
Member

@mingxoxo mingxoxo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

늦장 부리다가 PR 리뷰가 꽤나 시간이 걸렸네요 🥹 죄송합니다.. ㅎㅎ

저도 이전에 풀었던 분할정복 문제이네요!

분할정복 알고리즘으로 작성하더라도 재귀를 쓰다 보니 항상 비슷하다는 생각을 가지고 있었는데, 참고자료에 작성해주신 차이점을 한번 더 복기하고 갑니다..

아진님이 작성해주신 풀이도 재귀를 사용했지만 큰 부분을 먼저 확인하고 들어 간다는 점이 분할 정복이랑은 다른 것 같아요. 분할정복을 사용하면 반복되는 탐색이 불필요하게 발생하지 않는다는 점이 좋은 것 같습니다! 저도 아직 시간 복잡도를 계산하는 부분을 깊게 하지 못하는 것 같아서 실행 시간에 주의를 기울이는 습관을 들여야겠어요!

너무 수고하셨습니다 ~~! 👍🏻

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💻 BOJ 백준 온라인 저지의 문제 ✨ CPP C++로 푼 문제

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants