💯Problem Solving

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

date
Jul 29, 2023
slug
boj-20056
author
status
Public
tags
Python
BOJ
summary
백준 20056번 문제풀이입니다.
type
Post
thumbnail
category
💯Problem Solving
updatedAt
Jul 29, 2023 04:39 AM

문제 링크


문제


어른 상어가 마법사가 되었고, 파이어볼을 배웠다.
마법사 상어가 크기가 N×N인 격자에 파이어볼 M개를 발사했다. 가장 처음에 파이어볼은 각자 위치에서 이동을 대기하고 있다. i번 파이어볼의 위치는 (ri, ci), 질량은 mi이고, 방향은 di, 속력은 si이다. 위치 (r, c)는 r행 c열을 의미한다.
격자의 행과 열은 1번부터 N번까지 번호가 매겨져 있고, 1번 행은 N번과 연결되어 있고, 1번 열은 N번 열과 연결되어 있다.
파이어볼의 방향은 어떤 칸과 인접한 8개의 칸의 방향을 의미하며, 정수로는 다음과 같다.
7
0
1
6
2
5
4
3
마법사 상어가 모든 파이어볼에게 이동을 명령하면 다음이 일들이 일어난다.
  1. 모든 파이어볼이 자신의 방향 d로 속력 s칸 만큼 이동한다.
      • 이동하는 중에는 같은 칸에 여러 개의 파이어볼이 있을 수도 있다.
  1. 이동이 모두 끝난 뒤, 2개 이상의 파이어볼이 있는 칸에서는 다음과 같은 일이 일어난다.
    1. 같은 칸에 있는 파이어볼은 모두 하나로 합쳐진다.
    2. 파이어볼은 4개의 파이어볼로 나누어진다.
    3. 나누어진 파이어볼의 질량, 속력, 방향은 다음과 같다.
      1. 질량은 ⌊(합쳐진 파이어볼 질량의 합)/5⌋이다.
      2. 속력은 ⌊(합쳐진 파이어볼 속력의 합)/(합쳐진 파이어볼의 개수)⌋이다.
      3. 합쳐지는 파이어볼의 방향이 모두 홀수이거나 모두 짝수이면, 방향은 0, 2, 4, 6이 되고, 그렇지 않으면 1, 3, 5, 7이 된다.
    4. 질량이 0인 파이어볼은 소멸되어 없어진다.
마법사 상어가 이동을 K번 명령한 후, 남아있는 파이어볼 질량의 합을 구해보자.

문제풀이


N이 50이라 3중 포문을 사용해도 시간초과없이 해결 가능하다
 
먼저 input으로 arr[r][c]에 값을 입력해주고 ((m,s,d)) tuple
 
매 초마다 다음과 같은 연산을 수행한다.
  1. arr 배열에 있는 파이어볼에 대하여 파이어볼은 여러개 있을 수 있으므로 arr[r][c]에 있는 모든 파이어볼을 대상으로 한다.
    1. (r,c,s,c)에 대한 target_r, target_c 값을 계산한다.
    2. 이때 1~N은 이어져 있으므로 계산에 유의하여야 하는데
    3. 역방향으로 진행할때는 (c,r)-s%N으로 계산을 먼저해주고 0보다 낮은 값을 가질경우에는 N을 더해주어 보정해준다.
    4. 순방향으로 진행할 떄는 (c,r)+s%N으로 계산을 먼저해주고 N+1보다 높은 값을 가질떄는 %N 연산을 해주어 보정을 해준다. r,c값을 따로 계산해줬기 때문에 대각선이 아니면 둘 중 하나는 값을 고정해야한다.
  1. target_r, target_c 값이 계산되었으면, 파이어볼을 이동시킨 pending_list배열을 만들어서 넣어주고. pending_list[r][c]에 해당하는 모든 파이어볼에 대하여
    1. 만약 갯수가 2개 이상이면 확산한다는 의미이므로 확산코드인 spreading()을 호출해준다, 이때 속도, 질량, 방향은 문제에 써있는대로 게산하되 방향값 같은 경우 한번더 계산해준후에 속도, 질량, 방향 리스트를 반환해준다.
    2. 모든 방향 값에 대하여 arr[r][c]에 계산된 질량, 속도, 방향 값을 넣어준후 종료 만약 질량이 0일 경우에는 값을 넣어주지 않는다 ( 증발 )
    3. 갯수가 1개면 따로 계산할 필요없이 arr[r][c]에 질량, 속도, 방향 값을 넣어준후 종료
      1.  
 
파이썬 문제풀이 코드
import sys input = sys.stdin.readline N,M,K = tuple(map(int, input().split())) # Input arr = [[ [] for _ in range(N+1)] for _ in range(N+1)] for i in range(M): r,c,m,s,d = tuple(map(int, input().split())) arr[r][c].append((m,s,d)) # print(arr) # fireball move def get_target_xy(r, c, s, d): target_r, target_c = 0, 0 control = s%N if d == 7 or d == 6 or d == 5: target_c = c-control if target_c <= 0: target_c = N + target_c elif d == 1 or d == 2 or d == 3: target_c = c+control if target_c >= N+1: target_c = (c+control)%N else: target_c = c if d == 5 or d == 4 or d == 3: target_r = r+control if target_r >= N+1: target_r = (r+control)%N elif d == 7 or d == 0 or d == 1: target_r = r-control if target_r <= 0: target_r = N + target_r else: target_r = r # print(r,c,s,d,"-> ",target_r,target_c) return target_r, target_c def spreading(m_list, s_list, d_list): all_m = sum(m_list)//5 all_s = sum(s_list)//len(m_list) all_d = [0, 2, 4, 6] odd = 0 even = 0 for r_d in d_list: if r_d % 2 == 0: even += 1 else: odd += 1 if odd == 0 or even == 0: all_d = [0, 2, 4, 6] # 0 2 4 6 else: all_d = [1, 3, 5, 7] # 1 3 5 7 return all_m, all_s, all_d for _ in range(K): pending_list = [[ [] for _ in range(N+1)] for _ in range(N+1)] for r in range(1, N+1): # 1234 if N == 4 for c in range(1, N+1): if len(arr[r][c]) >= 1: # 파이어볼이 하나라도 있을 경우 for fireball in arr[r][c]: m,s,d = fireball # target_r, target_c = get_target_xy(r,c,s,d) pending_list[target_r][target_c].append((m,s,d)) # fireball이 이동 # print(target_r, target_c) # 이동후 확산 arr = [[ [] for _ in range(N+1)] for _ in range(N+1)] # 초기화 # print(pending_list) for r in range(1, N+1): # 1234 if N == 4 for c in range(1, N+1): # print(r, c, pending_list[r][c]) if len(pending_list[r][c]) >= 2: # 합쳐지는 경우에만 # print("합쳐지는경우") m_list, s_list, d_list = [], [], [] for fireball in pending_list[r][c]: m,s,d = fireball m_list.append(m) s_list.append(s) d_list.append(d) all_m, all_s, all_d = spreading(m_list, s_list, d_list) # print(all_m, all_s, all_d) for d in all_d: if all_m == 0: # 소멸 break else: arr[r][c].append((all_m, all_s, d)) # 방향에따른 4가지 파이어볼 추가 elif len(pending_list[r][c]) == 1: fireball = pending_list[r][c][0] m,s,d = fireball arr[r][c].append((m,s,d)) # elif len(pending_list[r][c]) == 1: # fireball = pending_list[r][c][0] # m,s,d = fireball # arr[r][c] # 남아있는 파이어볼 합 출력 fireball_sum = 0 for r in range(1, N+1): for c in range(1, N+1): for fireball in arr[r][c]: m, _, _ = fireball fireball_sum += m print(fireball_sum)
 
시간복잡도

    입력


    첫째 줄에 N, M, K가 주어진다.
    둘째 줄부터 M개의 줄에 파이어볼의 정보가 한 줄에 하나씩 주어진다. 파이어볼의 정보는 다섯 정수 ri, ci, mi, si, di로 이루어져 있다.
    서로 다른 두 파이어볼의 위치가 같은 경우는 입력으로 주어지지 않는다.

    출력


    마법사 상어가 이동을 K번 명령한 후, 남아있는 파이어볼 질량의 합을 출력한다.

    예제 입력


    4 2 1 1 1 5 2 2 1 4 7 1 6

    예제 출력


    8
    4 2 2 1 1 5 2 2 1 4 7 1 6
    8
    7 5 3 1 3 5 2 4 2 3 5 2 6 5 2 9 1 7 6 2 1 3 5 4 4 2 4 2
    9

    알고리즘 분류