Computer Science/Algorithm

[Python 3] BOJ 23288 주사위 굴리기 2

무니화니 2024. 3. 5. 15:55

최근 구현 문제를 푸는데 있어서 부족함이 있음을 깨닫고, 복잡하고 긴 글을 가진 문제를 정확하게 해석하는 문제들을 해결하려고 노력 중이다. 

바로 이 주사위 굴리기 2 같은 문제이다.

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

 

23288번: 주사위 굴리기 2

크기가 N×M인 지도가 존재한다. 지도의 오른쪽은 동쪽, 위쪽은 북쪽이다. 지도의 좌표는 (r, c)로 나타내며, r는 북쪽으로부터 떨어진 칸의 개수, c는 서쪽으로부터 떨어진 칸의 개수이다. 가장 왼

www.acmicpc.net

 

 

문제가 정말 친절하면서도, 복잡하다.

천천히 독해해보도록 하자.

먼저 보드게임판이 있다고 상상을 해보자. 맨 위쪽 끝에 주사위가 위치해있다.

1가 맨 위에 그려져있고, 6이 바닥에 있다. 

처음에는, 오른쪽으로 구른다. 그러면, 오른쪽에 그려져있는 3이 밑으로 위치한다. 

그 이후, 아랫면에 있는 3이랑, 보드게임판 1이랑 크기의 차이를 비교하여, 이동 방향 변화를 맞춘다.

이후, 보드게임판의 1이 어떻게 이어져있는지 파악한다. 이어진 같은 수의 개수 만큼, 수를 곱해서 총 점수에 더해준다.

 

n,m,k=map(int,input().split())
data=list(list(map(int,input().split())) for _ in range(n))
from collections import deque
dice=[0,2,4,1,3,5,6]
d=0
x,y=0,0
answer=0
def roll(d):
    if d==0: #east
        dice[4],dice[3],dice[2],dice[6]=dice[3],dice[2],dice[6],dice[4]
    elif d==1: #south
        dice[1],dice[3],dice[5],dice[6]=dice[6],dice[1],dice[3],dice[5]
    elif d==2: #west
        dice[4],dice[3],dice[2], dice[6]=dice[6],dice[4],dice[3],dice[2]
    else: #north
        dice[1],dice[3],dice[5],dice[6] = dice[3],dice[5],dice[6],dice[1]

def play():
    global d,answer,x,y
    nx,ny=x,y
    if d==0:
        nx,ny=x,y+1
        if 0<=nx<n and 0<=ny<m:
            roll(d)
            answer+=data[nx][ny]*score(nx,ny)
        else:
            d=2
            play()
            return
    elif d==1:
        nx,ny=x+1,y
        if 0<=nx<n and 0<=ny<m:
            roll(d)
            answer+=data[nx][ny]*score(nx,ny)
        else:
            d=3
            play()
            return
    elif d==2:
        nx,ny=x,y-1
        if 0<=nx<n and 0<=ny<m:
            roll(d)
            answer+=data[nx][ny]*score(nx,ny)
        else:
            d=0
            play()
            return
    else:
        nx,ny=x-1,y
        if 0<=nx<n and 0<=ny<m:
            roll(d)
            answer+=data[nx][ny]*score(nx,ny)
        else:
            d=1
            play()
            return
    
    if dice[6]>data[nx][ny]:
        d+=1
        if d==4:
            d=0

    elif dice[6]<data[nx][ny]:
        d-=1
        if d==-1:
            d=3
    x,y=nx,ny
    return

dx=[0,0,-1,1]
dy=[-1,1,0,0]
def score(xx,yy):
    count=1
    visited=list(list(0 for _ in range(m)) for _ in range(n))
    q=deque()
    q.append([xx,yy])
    visited[xx][yy]=1
    while q:
        x,y=q.popleft()
        for i in range(4):
            nx=x+dx[i]
            ny=y+dy[i]
            if 0<=nx<n and 0<=ny<m and not visited[nx][ny] and data[nx][ny]==data[xx][yy]:
                q.append([nx,ny])
                count+=1
                visited[nx][ny]=1
    return count

for _ in range(k):
    play()

print(answer)

 

이런 복잡한 문제가 나올 때일수록, 각 역할을 하는 함수로 분할하는 습관을 들여야 할 것이다. 

먼저 초기 주사위에 대한 변수 dice가 있다. dice의 index는 주사위의 위치를 임의로 적은 것이고, 숫자는 눈을 의미한다.

 

roll 함수는 동, 남 , 서, 북 방향으로 주사위가 굴렀을 때 일어나는 주사위의 변화를 저장한다.

 

play는 실제로 주사위를 해당 방향으로 굴리는 함수이다. 

이 때 두가지 경우가 존재한다.

첫 째로는, 실제로 진행하고자 하는 방향으로 주사위가 구를 수 있는 경우이다. 진짜로 굴려서, 점수를 찾는다.

두번째는, 진행하고자 하는 방향이 판 밖으로 나가야하는 경우이다. 이럴 때는, 가려고 한 곳의 반대편으로 돌아야한다. 

방향으로 반대로 바꾸고, 다시 play 함수를 재귀적으로 실행하자. 판은 최소 2 by 2이기 때문에, 방향을 바꿨을 때 또 벽에 막히는 경우는 존재하지 않는다.

이후 score 함수를 통해서 점수를 계산한다. 이 때, bfs를 사용해서 해당 발판과 이어져있는 숫자들의 수가 몇개인지 파악할 수 있다.

 

play 함수의 마지막 부분에서는, 해당 점수 매기기가 끝나고 다음 주사위의 이동 방향을 결정해준다.

현재 주사위 밑에 있는 값과 보드에서의 값을 비교하여, 방향을 정해주자.

 

해당 play를 k번 진행하면, 총 얻을 수 있는 점수를 파악할 수 있다.

 

P.S. 나는 <= 에서 =을  빼먹어서 무한 루프가 나는 걸 파악하기 위해 1시간을 소비했다...