> 파이썬, 구현, 시뮬레이션
📖 문제 : 마법사 상어와 파이어볼 (Python)
어른 상어가 마법사가 되었고, 파이어볼을 배웠다.
마법사 상어가 크기가 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
예제 입력 1
4 2 1
1 1 5 2 2
1 4 7 1 6
예제 출력 1
8
예제 입력 2
4 2 2
1 1 5 2 2
1 4 7 1 6
예제 출력 2
8
내 코드 (예제 4번 안풀림)
dy = [-1, -1, 0, 1, 1, 1, 0, -1]
dx = [0, 1, 1, 1, 0, -1, -1, -1]
N, M, K = map(int, input().split()) # N: 격자 크기, M: 파이어볼 갯수, K: 명령 횟수
# fireball[i][0]: i번째 파이어볼의 행 (r)
# fireball[i][1]: i번째 파이어볼의 열 (c)
# fireball[i][2]: i번째 파이어볼의 질량 (m)
# fireball[i][3]: i번째 파이어볼의 속력 (s)
# fireball[i][4]: i번째 파이어볼의 방향 (d)
fireball = []
for _ in range(M):
r, c, m, s, d = map(int, input().split())
fireball.append([r - 1, c - 1, m, s, d])
board = [[0] * N for _ in range(N)]
# board_info[(r, c)] = [cnt, m, s, d]
# 파이어볼의 위치 r행, c열
# 하나의 칸에 존재하는 파이어볼들의 갯수(cnt), 하나의 칸에 존재하는 파이어볼들의 질량 합(m),
# 하나의 칸에 존재하는 파이어볼들의 속력 합(s), 하나의 칸에 존재하는 파이어볼들의 방향의 방향이 같은지 다른지에 관한 수 (0: 다 짝수, 1: 다 홀수, 3: 방향 다름, 4: 리셋 값)
board_dic = [[0] * N for _ in range(N)]
board_info = dict()
result = 0
def board_add(r, c, m, s, d):
global board_info
if board_info.get((r, c), [0, 0, 0, 0]) == [0, 0, 0, 0]:
board_info[(r, c)] = [1, m, s, d % 2]
elif board_info[(r, c)] == [0, 0, 0, 4]:
board_info[(r, c)] = [1, m, s, d % 2]
else:
board_info[(r, c)][0] += 1
board_info[(r, c)][1] += m
board_info[(r, c)][2] += s
if board_info[(r, c)][3] != d % 2 and board_info[(r, c)][3] != 4:
board_info[(r, c)][3] = 3
elif board_info[(r, c)][3] == 4:
board_info[(r, c)][3] = d % 2
elif board_info[(r, c)][3] == d % 2:
board_info[(r, c)][3] = d % 2
# def board_remove(r, c):
# global board_info
# board_info[(r, c)] = [0, 0, 0, 4]
# 제일 처음 주어진 파이어볼에 대해 정보 입력
for f in fireball:
board[f[0]][f[1]] += 1 # board 칸의 파이어볼 갯수 + 1
board_dic[f[0]][f[1]] = f[4]
board_add(f[0], f[1], f[2], f[3], f[4])
while K:
# 모든 파이어볼 자신의 방향과 속력만큼 이동
for f in fireball:
board[f[0]][f[1]] -= 1
f[0] = ((f[0]) + dy[f[4]] * f[3]) % N
f[1] = ((f[1]) + dx[f[4]] * f[3]) % N
board[f[0]][f[1]] += 1
board_dic[f[0]][f[1]] = f[4]
board_add(f[0], f[1], f[2], f[3], f[4])
fireball.clear()
for y in range(N):
for x in range(N):
# 같은 칸에 파이어볼이 2개 이상이며, 합쳐진 파이어볼의 질량의 합/5가 0보다 큰 경우 => 4개의 파이어볼로 분해
if (y, x) not in board_info:
board[y][x] = 0
elif board[y][x] > 1:
if (board_info[(y, x)][1] // 5 > 0) and (board_info[(y, x)][3] != 4):
# 합쳐진 파이어볼의 방향이 홀, 짝이 달랐을 때
if board_info[(y, x)][3] == 3:
for i in range(4):
fireball.append(
[y, x, board_info[(y, x)][1] // 5, board_info[(y, x)][2] // board_info[(y, x)][0],
i * 2 + 1])
# 합쳐진 파이어볼의 방향이 홀홀홀 이거나 짝짝짝으로 같았을 때
board[y][x] = 4
else:
for i in range(4):
fireball.append(
[y, x, board_info[(y, x)][1] // 5, board_info[(y, x)][2] // board_info[(y, x)][0],
i * 2])
board[y][x] = 4
elif board[y][x] == 1 and board_info[(y, x)][1] > 0:
fireball.append([y, x, board_info[(y, x)][1], board_info[(y, x)][2], board_dic[y][x]])
board[y][x] = 1
board_info.clear()
K -= 1
for f in fireball:
result += f[2]
print(result)
다른 사람 코드
dy = [-1, -1, 0, 1, 1, 1, 0, -1]
dx = [0, 1, 1, 1, 0, -1, -1, -1]
N, M, K = map(int, input().split())
fireballs = []
board = [[[] for _ in range(N)] for _ in range(N)]
for _ in range(M):
_r, _c, _m, _s, _d = map(int, input().split())
fireballs.append([_r + 1, _c + 1, _m, _s, _d])
for _ in range(K):
while fireballs:
_r, _c, _m, _s, _d = fireballs.pop()
nr = (_r + dy[_d] * _s) % N
nc = (_c + dx[_d] * _s) % N
board[nr][nc].append([_m, _s, _d])
for y in range(N):
for x in range(N):
if len(board[y][x]) > 1:
sum_m, sum_s, cnt_even, cnt_odd, cnt = 0, 0, 0, 0, len(board[y][x])
while board[y][x]:
_m, _s, _d = board[y][x].pop()
sum_m += _m
sum_s += _s
if _d % 2 == 0:
cnt_even += 1
else:
cnt_odd += 1
if cnt == cnt_even or cnt == cnt_odd:
new_d = [0, 2, 4, 6]
else:
new_d = [1, 3, 5, 7]
if sum_m // 5 > 0:
for d in new_d:
fireballs.append([y, x, sum_m//5, sum_s//cnt, d])
if len(board[y][x]) == 1:
fireballs.append([y, x] + board[y][x].pop())
print(sum(f[2] for f in fireballs))
Point ! ⭐️
- 풀이시간 ⏰ 3시간 넘음 ! 심지어 예제 4번은 틀림 . .
3차원 배열에 익숙치 않아 board_info 라는 리스트를 또 만들다보니
변수들이 너무 많아 머리가 터질 뻔 했다 🤯
다른 사람 코드를 보니 깰끔 그 잡채 보고는 이렇게 짧고 코드가 간단한 걸 보니
다른 골드 문제에 비해 쉬운 문제인지 알겠더라 . . (난 머리 터졌는데 !)
그래서 다른 분 코드만 5번 다시 써보며 공부했당.
이 코드에서 배울 점은,
1. 3차원 배열로 board의 행렬에 따른 fireballs에 대한 정보 저장을 가능케 하는 것
board = [[[] for _ in range(N)] for _ in range(N)]
board[nr][nc].append([_m, _s, _d])
if len(board[y][x]) > 1:
while board[y][x]:
_m, _s, _d = board[y][x].pop()
2. pop()을 사용하여 fireball이 이동할 때 (원래 자리의 fireball을 없애고, 다른 새로운 자리로 추가)의 시뮬레이션을 단순화
3. sum(f[2] for f in fireballs) 처럼 sum함수를 간단하게 사용하기
print(sum(f[2] for f in fireballs))
추가적으로,
내가 평소에 헷갈렷던 2차원 배열에서의 시뮬레이션 구현시에
행렬을 y ,x로 바꿔 쓰는 것과 0번 행이 N번 행과 연결, 0번 열이 N번 열과 연결되어있을 때의
배열 위의 어떤 것이 이동하기 위한 그 행렬의 위치좌표를 구하는 것에 대해 정리해보았다.
🐰 1. 2차원 배열 행렬을 y, x로 바꿔 쓰는 이유
이렇기 때문에 항상 2차원 배열을 탐색할 때에는 행(바깥) -> 열(안쪽 : 그 행의 원소들)으로 탐색해야한다.
for y in range(N):
for x in range(N):
if board[y][x] == 0:
🐰 2. 0번 행이 N번 행과 연결, 0번 열이 N번 열과 연결되어있을 때 위치좌표를 구하기
yy = (y + dy[i] * a) % N
xx = (x + dx[i] * a) % N
백준
https://www.acmicpc.net/problem/20056
참고
'SAMSUNG > 삼성 SW 역량 테스트 준비' 카테고리의 다른 글
[백준 14503번] 로봇 청소기 (Python) (1) | 2023.10.13 |
---|---|
[백준 14499번] 주사위 굴리기 (Python) (1) | 2023.10.13 |
[백준 13458번] 시험 감독 (Python) (0) | 2023.10.13 |
순열과 조합 (직접 구현 & itertools 라이브러리) (0) | 2023.10.13 |
[백준 20055번] 컨베이어 벨트 위의 로봇 (Python) (0) | 2023.10.12 |