BOJ - 20061 - 모노미노도미노2

Jeuk Oh·2021년 9월 7일
0

코딩문제풀이

목록 보기
11/14

문제

https://www.acmicpc.net/problem/14499

문제가 참 길다.

풀이

아이디어

파란 칸과 초록 칸의 작동 원리는 블록이 차는 방향만 다를 뿐 똑같다. 블록이 차는 방향은 통일해버리고 대신 인풋을 회전해서 각각 넣어주면, 구현 하나에 두가지 케이스를 다 다룰 수 있을 것이다.

주어진 문제를 이렇게 생각하면, 인풋을 바꾸는 함수 1개와

문제 규칙을 하나에 대해서만 구현하는 클래스만 집중하면 될 듯 하다.

행이나 열이 타일로 가득찬 경우와 연한 칸에 블록이 있는 경우가 동시에 발생할 수 있다. 이 경우에는 행이나 열이 타일로 가득 찬 경우가 없을 때까지 점수를 획득하는 과정이 모두 진행된 후, 연한 칸에 블록이 있는 경우를 처리해야 한다.

구현해야 하는 기능은
1. 블록이 떨어져서 멈추는 것.
2. 행이 가득차면 점수를 올리고 행을 지운 뒤 윗 행을 당기는 기능
3. 흰 칸에 블록이 찬 상태면 흰 칸에 블록이 없을 때 까지 행을 미는 기능

실행 순서는 1 -> 변화가 없을 때까지 2 반복 -> 변화가 없을 때까지 3 반복 -> 다시 1

이러면 될 것 같다.

다시 생각해보니 굳이 2와 3을 반복할 필요 없이 그냥 2를 적용한 뒤 3을 해도 상관 없을 것 같다. 케이스를 분류해면,

  1. 흰칸 x 완성 x -> 그냥 계속 쌓으면 된다.
  2. 흰칸 x 완성 o -> 완성 된 줄을 지우고 한 줄 밀어주면 된다
  3. 흰칸 o 완성 x -> 흰칸이 없어지게 밀어주면 된다.
  4. 흰칸 o 완성 o -> 완성을 지운 뒤 그 뒤에도 흰칸이 있다면 밀어주면 된다.

어차피 2번을 한 뒤 1,3 에 대해서 구분해서 밀어주는 행동을 하면 되서 굳이 반복할 필요가 없다. 흰 칸의 줄이 완성되는 것도 불가능하여 예외가 없다.

따라서 그냥 실행 순서는 1 -> 2 -> 3 ->

만 반복해도 된다.


구현

먼저 인풋을 돌려주는 함수를 만들어보자

x,y를 4x4에서 박스에서 90도 회전한 것과 같다.
t = 1 이면 그대로 t= 1
t = 2 이면 t = 3
t = 3 일땐 t = 2 가 된다.

def rotate(x,y,t):
    if t == 1 or t == 2:
        nx, ny = y, 3-x
    else:
        nx, ny = y, 3-x-1
    nt = [1,3,2][t-1]
    return nt,nx,ny

t = 3일때는 90도 돌린 뒤 좌표가 누운 블럭의 왼쪽을 가르키는 게 아니라 오른쪽 블럭을 가르키므로 y 좌표에 1을 더 빼준다. 역시 하나하나씩 구현을 하면서 확인하는 과정이 중요하다.

#input
2 3 0
3 2 2
3 2 3

#output
(3, 0, 0)
(2, 2, 0)
(2, 3, 0)

잘 된다.

이제 클래스를 짜고, 일단 블럭을 내리는 부분 까지 구현해보자

class Board:
   def __init__(self):
       self.board = [[0]*4 for _ in range(6)]
       self.score = 0

   def putblock(self,t,x,y):
       if t == 1:
           check = [(1,0)]
       elif t == 2:
           check = [(1,0),(1,1)]
       else:
           check = [(2,0),(1,0)]
           
       nx, ny = 0, y
       flag = True
       while flag and nx < 6:
           for _ in check:
               if 6 <= nx+_[0] or self.board[nx+_[0]][ny+_[1]]:
                   flag = False
                   break
           else:
               nx += 1
       for _ in check:
           self.board[nx+_[0]-1][ny+_[1]] = 1

       pprint(self.board)

초큼 지저분하다.
커서보다 한칸 아래 칸을 탐색하도록 check 리스트를 만들고, check 리스트가 1과 만나거나 탐색하는 범위가 바닥을 뚫을 때까지 nx를 1씩 키워가면서 확인, 체크가 1과 만나면 그자리에 멈춘 커서에 board를 채워주면 되고, 체크가 바닥을 뚫고 나가면 그자리에서 멈추고 채워주면 된다.

# input
4
2 3 0
3 2 2
3 2 3
3 3 0

#output
[[0, 0, 0, 0],
 [0, 0, 0, 0],
 [0, 0, 0, 0],
 [0, 0, 0, 0],
 [0, 0, 0, 0],
 [1, 1, 0, 0]]
 
[[0, 0, 0, 0],
 [0, 0, 0, 0],
 [0, 0, 0, 0],
 [0, 0, 0, 0],
 [0, 0, 1, 0],
 [1, 1, 1, 0]]
 
[[0, 0, 0, 0],
 [0, 0, 0, 0],
 [0, 0, 0, 0],
 [0, 0, 0, 0],
 [0, 0, 1, 1],
 [1, 1, 1, 1]]
 
[[0, 0, 0, 0],
 [0, 0, 0, 0],
 [0, 0, 0, 0],
 [1, 0, 0, 0],
 [1, 0, 1, 1],
 [1, 1, 1, 1]]

잘 된다 !

행이 가득차면 점수를 올리고 행을 지운 뒤 윗 행을 당기는 기능


    def scoring(self):
       for idx in range(2,6):
           if all(self.board[idx]):
               self.score += 1
               del self.board[idx]
               self.board.insert(0, [0,0,0,0])

구현 문제니 최적화 생각하지 말고 그냥 지우고 위에 빈 행을 삽입해주었다.

흰 칸에 블록이 찬 상태면 흰 칸에 블록이 없을 때 까지 행을 미는 기능

    def regul(self):
       for idx in range(0,2):
           if any(self.board[idx]):
               del self.board[-1]
               self.board.insert(0, [0, 0, 0, 0])

마찬가지로 그냥 제일 뒤 행을 지우고 빈 행을 앞에 삽입..


    def ordering(self,t,x,y):
       self.putblock(t,x,y)
       self.scoring()
       self.regul()

   def printing(self):
       cnt = 0
       for idx in range(2, 6):
           cnt += sum(self.board[idx])
       return self.score, cnt

마지막으로 앞서 구현한 함수를 순서대로 실행하는 함수랑
점수와 1의 개수를 모으는 프린트 함수

N = int(readline().rstrip())
order = [list(map(int,readline().split())) for _ in range(N)]

A = Board()
B = Board()

for t,x,y in order:
   nt,nx,ny = (rotate(x,y,t))
   A.ordering(t,x,y)
   B.ordering(nt,nx,ny)

A_s, A_c = A.printing()
B_s, B_c = B.printing()
print(A_s+B_s)
print(A_c+B_c)

메인 부분, 뭐... A 랑 B 보드를 만들어서 A에는 원래 명령을 B에는 회전을 고려한 새로운 명령을 다 넣고 프린트 한 값을 더해서 출력해주면 끝


결과

역시나 차근차근 하나씩 짜면 구현은 거진 다 되더라..

오히려 dp나 수학이 넘 어렵다 ㅜ

바로 전 문제보단 어렵고 더 재밌다!

profile
개발을 재밌게 하고싶습니다.

0개의 댓글