링크 : www.acmicpc.net/problem/20056
🎈문제
어른 상어가 마법사가 되었고, 파이어볼을 배웠다.
마법사 상어가 크기가 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 |
마법사 상어가 모든 파이어볼에게 이동을 명령하면 다음이 일들이 일어난다.
- 모든 파이어볼이 자신의 방향 di로 속력 si칸 만큼 이동한다.
- 이동하는 중에는 같은 칸에 여러 개의 파이어볼이 있을 수도 있다.
- 이동이 모두 끝난 뒤, 2개 이상의 파이어볼이 있는 칸에서는 다음과 같은 일이 일어난다.
- 같은 칸에 있는 파이어볼은 모두 하나로 합쳐진다.
- 파이어볼은 4개의 파이어볼로 나누어진다.
- 나누어진 파이어볼의 질량, 속력, 방향은 다음과 같다.
- 질량은 ⌊(합쳐진 파이어볼 질량의 합)/5⌋이다.
- 속력은 ⌊(합쳐진 파이어볼 속력의 합)/(합쳐진 파이어볼의 개수)⌋이다.
- 합쳐지는 파이어볼의 방향이 모두 홀수이거나 모두 짝수이면, 방향은 0, 2, 4, 6이 되고, 그렇지 않으면 1, 3, 5, 7이 된다.
- 질량이 0인 파이어볼은 소멸되어 없어진다.
마법사 상어가 이동을 K번 명령한 후, 남아있는 파이어볼 질량의 합을 구해보자.
🎈입력
첫째 줄에 N, M, K가 주어진다.
둘째 줄부터 M개의 줄에 파이어볼의 정보가 한 줄에 하나씩 주어진다. 파이어볼의 정보는 다섯 정수 ri, ci, mi, si, di로 이루어져 있다.
서로 다른 두 파이어볼의 위치가 같은 경우는 입력으로 주어지지 않는다.
🎈출력
마법사 상어가 이동을 K번 명령한 후, 남아있는 파이어볼 질량의 합을 출력한다.
🎈제한
- 4 ≤ N ≤ 50
- 0 ≤ M ≤ N2
- 1 ≤ K ≤ 1,000
- 1 ≤ ri, ci ≤ N
- 1 ≤ mi ≤ 1,000
- 1 ≤ si ≤ 1,000
- 0 ≤ di ≤ 7
🎈구현
사용한 라이브러리
from collections import defaultdict
collections 라이브러리의 defaultdict 모듈을 사용해서 파이어볼의 정보를 받아오는 딕셔너리를 정의했습니다.
defaultdict을 사용하면 딕셔너리 value의 타입을 미리 정의해놓을 수 있습니다.
입력 값 처리
N, M, K = map(int, input().split())
info = defaultdict(list)
for _ in range(M):
r, c, m, s, d = map(int, input().split())
info[(r, c)].append([m, s, d])
direction = {
0: (-1, 0),
1: (-1, 1),
2: (0, 1),
3: (1, 1),
4: (1, 0),
5: (1, -1),
6: (0, -1),
7: (-1, -1)
}
info는 defaultdict를 사용해 선언했으므로 초기화를 하지 않아도 됩니다.
파이어볼의 방향을 미리 딕셔너리에 저장해두었습니다.
K 만큼 이동 명령 내려 딕셔너리 업데이트하기
def command(prev):
pass
for _ in range(K):
info = command(info)
mass = 0
for fireballs in info.values():
for fireball in fireballs:
mass += fireball[0]
print(mass)
마법사 상어가 이동을 K번 명령한 후, 남아있는 파이어볼 질량의 합을 구하는 것이 문제입니다.
따라서 명령에 의한 동작은 command 함수에서 하도록 했습니다.
원래는 배열로 만들어 풀까도 생각했지만 메모리 사용량도 그렇고, 배열을 만들어봤자 쓰는 부분이 별로 없을거같아서 딕셔너리로 풀었습니다.
[command] 업데이트할 defaultdict 선언
current = defaultdict(list)
for key in prev.keys():
for item in prev[key]:
current_r, current_c = key
si, di = item[1], item[2]
dr, dc = direction[di]
next_r, next_c = ((current_r+dr*si)%N+N)%N, ((current_c+dc*si)%N+N)%N
current[next_r, next_c].append(item)
추후 K번의 command에 따라 업데이트 할 defaultdict인 current를 선언합니다.
입력(prev)에 따라 파이어볼을 이동시켜줍니다.
next_r, next_c 연산을 할 때 ((current_r+dr*si)%N+N)%N 이렇게 모듈러 연산을 두번 한 이유는 current_r+dr*si 부분이 양수가 될 수도, 음수가 될 수도 있기때문입니다. 음수와 양수의 모듈러 연산 값을 같게하기 위해 모듈러 연산을 두번 해주었습니다.
[command] 파이어볼이 만났을 시 미니 파이어볼 만들기
dump = []
for key in current.keys():
fireballs = current[key]
num = len(fireballs)
if num>1:
current_r, current_c = key
mi, si, di = 0, 0, []
for fireball in fireballs:
mi += fireball[0]
si += fireball[1]
di.append(fireball[2])
mi = mi//5
if mi!=0:
si = si//num
next_di, current[key] = [], []
if all([d%2==0 for d in di]) or all([d%2!=0 for d in di]):
next_di = [0, 2, 4, 6]
else:
next_di = [1, 3, 5, 7]
for i in range(4):
current[key].append([mi, si, next_di[i]])
else:
dump.append(key)
for trash in dump:
del current[trash]
dump 는 추후 질량이 0인 파이어볼을 소멸시키기 위해 사용하는 쓰레기통입니다. 딕셔너리의 키 값을 사용하는 for 루프에서 딕셔너리 값을 바로 삭제하면 key error가 발생하므로 주의해야 합니다.
fireballs 는 현재 key 값(r, c)에 있는 파이어볼의 정보가 담긴 리스트입니다. fireballs의 길이가 2 이상이면 파이어볼이 충돌했다는 것을 의미합니다.
따라서 파이어볼을 합친 뒤 미니파이어볼로 나누어주는 작업을 합니다.
질량 m과 속도 s는 더해주기만 하면 되지만, 방향은 홀짝을 판별해야하기 때문에 리스트 타입을 사용했습니다.
all()은 인자로 받은 iterative가 전부 true면 true를 리턴해줍니다(any()는 하나라도 true면 true 리턴).
🎈전체 코드
from collections import defaultdict
def solution():
N, M, K = map(int, input().split())
info = defaultdict(list)
for _ in range(M):
r, c, m, s, d = map(int, input().split())
info[(r, c)].append([m, s, d])
direction = {
0: (-1, 0),
1: (-1, 1),
2: (0, 1),
3: (1, 1),
4: (1, 0),
5: (1, -1),
6: (0, -1),
7: (-1, -1)
}
def command(prev):
current = defaultdict(list)
for key in prev.keys():
for item in prev[key]:
current_r, current_c = key
si, di = item[1], item[2]
dr, dc = direction[di]
next_r, next_c = ((current_r+dr*si)%N+N)%N, ((current_c+dc*si)%N+N)%N
current[next_r, next_c].append(item)
dump = []
for key in current.keys():
fireballs = current[key]
num = len(fireballs)
if num>1:
current_r, current_c = key
mi, si, di = 0, 0, []
for fireball in fireballs:
mi += fireball[0]
si += fireball[1]
di.append(fireball[2])
mi = mi//5
if mi!=0:
si = si//num
next_di, current[key] = [], []
if all([d%2==0 for d in di]) or all([d%2!=0 for d in di]):
next_di = [0, 2, 4, 6]
else:
next_di = [1, 3, 5, 7]
for i in range(4):
current[key].append([mi, si, next_di[i]])
else:
dump.append(key)
for trash in dump:
del current[trash]
return current
for _ in range(K):
info = command(info)
mass = 0
for fireballs in info.values():
for fireball in fireballs:
mass += fireball[0]
print(mass)
solution()
'Algorithm > Python' 카테고리의 다른 글
[파이썬/백준 20058] 마법사 상어와 파이어스톰/런타임에러(RecursionError) 원인과 해결방법 (0) | 2021.03.25 |
---|---|
[파이썬/백준 20057] 마법사 상어와 토네이도 (0) | 2021.03.24 |
[파이썬/백준 1697] 숨박꼭질 BFS (0) | 2021.03.21 |
[파이썬/백준 2178] 미로 탐색 BFS (0) | 2021.03.21 |
[LeetCode - Top Interview Questions(Easy, Python3)]Strings (0) | 2021.03.14 |