Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
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
Tags
more
Archives
Today
Total
관리 메뉴

개발 블로그

[백준/파이썬] 2630번<색종이 만들기> 문제 풀이 본문

Algorithm

[백준/파이썬] 2630번<색종이 만들기> 문제 풀이

토산인 2023. 3. 3. 20:34
 

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로 주어지며, 각 숫자 사이에는 빈칸이 하나씩 있다.

출력

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

예제 입력 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등분하며 하얀색 또는 파란색 색종이가 될 때까지 등분하는구나

-> 큰 종이를 등분해 작은 종이를 구한다.. 이것은 분할정복?!ㅎㅎ

-> 종이를 현재 크기의 반 크기로 나눈다

-> 특정 색의 색종이가 될 때까지 나누고 최소한이 된다면 그만한다 

 

문제 핵심과 알고리즘 

큰 문제(큰 종이)를 작은 문제(작은 종이)로 나누는 (그렇다고 단순 재귀 함수 문제가 아닌) 분할 정복!

import sys
sys.setrecursionlimit(10**6)

n=int(input())
board=[]
for i in range(n) :
    board.append(list(map(int, input().split())))
cnt0,cnt1=0,0

def divide(board) :
    global cnt0
    global cnt1
    cnt=check(board)
    if cnt==0 :
        cnt0+=1
        return
    elif cnt==1 :
        cnt1+=1
        return

    size = len(board)//2
    #4등분한 각각의 board
    board1=[[0 for i in range(size)] for j in range(size)]
    board2 = [[0 for i in range(size)] for j in range(size)]
    board3 = [[0 for i in range(size)] for j in range(size)]
    board4 = [[0 for i in range(size)] for j in range(size)]
	
    for i in range(size) :
        for j in range(size) :
            board1[i][j]=board[i][j]
            board2[i][j] = board[i][j+size]
            board3[i][j] = board[i+size][j]
            board4[i][j] = board[i+size][j+size]
	#각 등분한 board에 대해 다시 등분 진행
    divide(board1)
    divide(board2)
    divide(board3)
    divide(board4)

#흰색 또는 파란색 색종이가 되는지 확인
def check(board) :
    for i in range(len(board)) :
        for j in range(len(board)) :
            if board[0][0]!=board[i][j] :
                return -1
    return board[0][0]

divide(board)
print(cnt0)
print(cnt1)

문제 풀면서 헤맸던 부분

1. global과 전역변수

  흰색 색종이와 파란색 색종이 수를 세는 cnt0, cnt1를 divide 함수 안에서 사용할 수가 없었다. 내가 리스트 변수랑 헷갈려서 지역변수랑 전역변수를 헷갈렸다. cnt0와 cnt1를 함수 내에서도 사용할 수 있도록 global로 선언했다. 

 

다른 사람 풀이 참고하며 배운 점

import sys

N = int(sys.stdin.readline())
paper = [list(map(int, sys.stdin.readline().split())) for _ in range(N)] 

result = []

def solution(x, y, N) :
  color = paper[x][y]
  for i in range(x, x+N) :
    for j in range(y, y+N) :
      if color != paper[i][j] :
        solution(x, y, N//2)
        solution(x, y+N//2, N//2)
        solution(x+N//2, y, N//2)
        solution(x+N//2, y+N//2, N//2)
        return
  if color == 0 :
    result.append(0)
  else :
    result.append(1)


solution(0,0,N)
print(result.count(0))
print(result.count(1))

우와,, 되게 간단하다.. 나는 함수를 호출할 때마다 board를 새로 만들어서 매개변수로 넘겼는데 절대 그럴 필요가 없었다.. 그냥 좌표로 넘겼다. 나는 매번 board를 만들어서 메모리도 많이 차지했을 뿐만 아니라 복잡했는데..

=> divide 할 때 기존의 큰 문제의 것을 이용하는 쪽으로 생각을 하자!