[BOJ] 20056 - 마법사 상어와 파이어볼 💥

김우경·2021년 4월 10일
0

삼성기출

목록 보기
16/37

삼성 기출 상어시리즈 첫 발,,

문제 링크

20056 - 마법사 상어와 파이어볼

문제 설명

마법사 상어가 N*N 크기의 격자에서 파이어볼 M개를 이동시킨다. 파이어볼 i는 초기에 (ri,ci)(r_i, c_i)의 위치에서 질량 mim_i, 방향 did_i, 속력 sis_i을 가지고 이는 입력으로 주어진다. 상어가 이동명령을 내리면

  1. 모든 파이어볼이 did_i의 방향으로 sis_i칸 이동
    -> 이동중에는 같은 칸에 여러 파이어볼이 존재 가능
  2. 이동 후 한칸에 두개 이상의 파이어볼이 있으면
    2.1 하나로 합치기
    2.2 4개의 파이어볼로 나누는데
    - 질량 = 합쳐진 파이어볼 질량의 총 합 / 5
    - 속력 = 합쳐진 파이어볼 속력의 총 합 / 합쳐진 파이어볼 개수
    - 방향 = 모든 파이어볼의 방향이 홀수거나 짝수면 0,2,4,6 아니면 1,3,5,7
    2.3 이때 질량이 0이 되어버리면 해당 파이어볼들은 소멸

이렇게 이동명령의 순서가 주어질때 k번의 명령 뒤 남은 파이어볼 질량의 총 합은?

문제 풀이

시키는 대로 하면 되는 시뮬레이션 문젠데,, 디버깅에서 한참 헤멨다 ^_ㅜ 이런 문제는 코드로 우선 적기보다 설계를 똑바로 작성하고 문제푸는 습관을 들여야 할듯 ,,,

  • board[i][j] : (i,j)의 위치에 있는 파이어볼 정보를 담는 리스트

입력받기

for i in range(M):
    r, c, m, s, d = map(int, input().split())
    board[r-1][c-1].append([m, s, d, -1])

1 ≤ ri, ci ≤ N이므로 r-1, c-1 처리 해줘야됨 ,,

i회 명령에서 파이어볼 이동

# 1. 모든 파이어볼에 대해서 방향 d로 s만큼 이동하기 
    for i in range(N):
        for j in range(N):
            for f in range(len(board[i][j])):
                fireball = board[i][j].pop(0)
                nx, ny = i, j
                m, s, d, moves = fireball
                if moves != move:
                    nx, ny = move_fireball(i, j, m, s, d)
                board[nx][ny].append([m, s, d, move])

move_fireball함수를 이용해서 각각의 파이어볼을 이동한 결과를 계산하고, 전체 격자에서 이 정보를 갱신해준다.

이동을 구현한 함수는 다음과 같다.

def move_fireball(x, y, m, s, d):
    # d방향으로 s만큼 이동시키기 (m, s, d)
    nx, ny = (x+dx[d]*s)%N, (y+dy[d]*s)%N
    return nx, ny

격자의 행과 열은 1번부터 N번까지 번호가 매겨져 있고, 1번 행은 N번과 연결되어 있고, 1번 열은 N번 열과 연결되어 있다.

라고 주어졌기 때문에, 원래 좌표+방향 d로 s만큼 이동한 값을 N으로 나눈 나머지가 좌표가 된다.

이동 후 한칸에 2개 이상 존재하는 파이어볼 처리

# 2. 이동 후 한칸에 2개이상 존재하는지
    for i in range(N):
        for j in range(N):
            if len(board[i][j]) > 1: 
                length = len(board[i][j])
                # 2개 이상이면
                sumM, sumS, oddcount, evencount = 0, 0, 0, 0
                for f in board[i][j]:
                    sumM += f[0]
                    sumS += f[1]
                    if f[2] % 2 == 0:
                        evencount += 1
                    else:
                        oddcount += 1
                # 나눈 뒤 질량이 0보다 크면 4개로 나누기
                if sumM // 5 != 0:
                    m, s = sumM//5, sumS//length
                    if oddcount == 0 or evencount == 0:
                        # 전부 홀수거나 짝수면
                        board[i][j] = [[m,s,0,move], [m,s,2,move], [m,s,4,move], [m,s,6,move]]
                    else:
                        board[i][j] = [[m,s,1,move], [m,s,3,move], [m,s,5,move], [m,s,7,move]]
                # 아니면 전체 소멸
                else:
                    board[i][j] = []

모든 격자를 돌며 칸에 파이어볼이 2개 이상이면 주어진대로 처리해준다. 해당 칸의 모든 파이어볼의 방향이 홀/짝인지 확인하는 부분은 oddcount와 evencount를 이용해서 구현했다. oddcount 또는 evencount가 해당 칸의 파이어볼 개수와 같으면 조건을 만족한다는 의미니까 방향을 0,2,4,6으로, 아니면 1,3,5,7로 append해줬다.

전체 코드

전체 코드는 다음과 같다.

import sys

input = sys.stdin.readline

N, M, K = map(int, input().split())
board = [[[] for _ in range(N)] for _ in range(N)]
dx = [-1, -1, 0, 1, 1, 1, 0, -1]
dy = [0, 1, 1, 1, 0, -1, -1, -1]

def move_fireball(x, y, m, s, d):
    # d방향으로 s만큼 이동시키기 (m, s, d)
    nx, ny = (x+dx[d]*s)%N, (y+dy[d]*s)%N
    return nx, ny

for i in range(M):
    r, c, m, s, d = map(int, input().split())
    board[r-1][c-1].append([m, s, d, -1])

for move in range(K):
    # 1. 모든 파이어볼에 대해서 방향 d로 s만큼 이동하기 
    for i in range(N):
        for j in range(N):
            for f in range(len(board[i][j])):
                fireball = board[i][j].pop(0)
                nx, ny = i, j
                m, s, d, moves = fireball
                if moves != move:
                    nx, ny = move_fireball(i, j, m, s, d)
                board[nx][ny].append([m, s, d, move])

    # 2. 이동 후 한칸에 2개이상 존재하는지
    for i in range(N):
        for j in range(N):
            if len(board[i][j]) > 1: 
                length = len(board[i][j])
                # 2개 이상이면
                sumM, sumS, oddcount, evencount = 0, 0, 0, 0
                for f in board[i][j]:
                    sumM += f[0]
                    sumS += f[1]
                    if f[2] % 2 == 0:
                        evencount += 1
                    else:
                        oddcount += 1
                # 나눈 뒤 질량이 0보다 크면 4개로 나누기
                if sumM // 5 != 0:
                    m, s = sumM//5, sumS//length
                    if oddcount == 0 or evencount == 0:
                        # 전부 홀수거나 짝수면
                        board[i][j] = [[m,s,0,move], [m,s,2,move], [m,s,4,move], [m,s,6,move]]
                    else:
                        board[i][j] = [[m,s,1,move], [m,s,3,move], [m,s,5,move], [m,s,7,move]]
                # 아니면 전체 소멸
                else:
                    board[i][j] = []

answer = 0
for i in range(N):
    for j in range(N):
        for f in board[i][j]:
            answer += f[0]

print(answer)

흐름이 긴 구현문제는 쉽지 않구만요,,

profile
Hongik CE

0개의 댓글