구현해야할 진행 순서는 다음과 같다.
처음에, 기본적으로 비바라기를 시전하면 (N,1) , (N,2), (N-1,1), (N-1,2) 에 비구름이 생긴다.
이제 구름이동을 M번 명령하므로, 다음 작업을 M번 수행하면 된다.
푸는데 한시간 정도 걸렸다. (사실 30분만에 풀었는데, % N을 % 5로 잘못 쓴걸 고치는데 30분이 걸렸다..ㅋㅋㅋ)
문제 조건 자체가 딱히 시간초과가 날 이유가 없어서 편하게 풀었던 것 같다.
# 모든 구름이 d 방향으로 s칸 이동
def move(d, s, inc_cloud):
global cloud
move_y = delta[d][0] * s
move_x = delta[d][1] * s
inc = []
while cloud:
y, x = cloud.pop()
ny = (y + move_y) % N
nx = (x + move_x) % N # 이동한 구름의 y,x 좌표
graph[ny][nx] += 1
inc.append([ny, nx]) # 새로 이동한 구름의 좌표
inc_cloud[ny][nx] = 1
cloud = inc
구름이 이동하고, 각 구름에서 비가 내려 물의 양을 1증가시켜준다.
그리고, 5번 함수의 조건을 위해, 이동한 좌표를 저장해 주었다. 사라지는 건 따로 구현하지 않아도 된다.(5번에서 덮어쓸 것)
def magic():
global cloud
d = [[-1, -1], [-1, 1], [1, -1], [1, 1]] # 대각선 경우의 수 4가지
for cur_y, cur_x in cloud:
for dy, dx in d:
ny = cur_y + dy
nx = cur_x + dx
if not check(nx, ny):
continue
if graph[ny][nx] > 0:
graph[cur_y][cur_x] += 1
대각선 경우 다 따져서 물복사 해주면 된다. 어차피 구름이 있는 좌표는 물 양이 무조건 1 이상 이기 때문에, lazy하게 하지 않아도 된다.
def create(old_cloud):
new_cloud = []
for yy in range(N):
for xx in range(N):
if graph[yy][xx] >= 2 and old_cloud[yy][xx] == 0:
new_cloud.append([yy, xx])
graph[yy][xx] -= 2
return new_cloud
구름을 새로 생성해줄 때, old_cloud = 0 인지 꼭 확인해준다.(1이면 3번에서 사라진 구름)
N, M = map(int, input().split())
graph = []
# [y,x]. 1부터 순서대로
delta = [[0, -1], [-1, -1], [-1, 0], [-1, 1], [0, 1], [1, 1], [1, 0], [1, -1]]
for _ in range(N):
inp = list(map(int, input().split()))
graph.append(inp)
move_op = []
for _ in range(M):
inp = list(map(int, input().split()))
move_op.append(inp)
# 처음에 생긴 구름
# --- 입력 끝 ---
# 모든 구름이 d 방향으로 s칸 이동
def move(d, s, inc_cloud):
global cloud
move_y = delta[d][0] * s
move_x = delta[d][1] * s
inc = []
while cloud:
y, x = cloud.pop()
ny = (y + move_y) % N
nx = (x + move_x) % N # 이동한 구름의 y,x 좌표
graph[ny][nx] += 1
inc.append([ny, nx]) # 새로 이동한 구름의 좌표
inc_cloud[ny][nx] = 1
cloud = inc
# 경계 넘어가는 지 확인
def check(x, y):
if 0 <= x < N and 0 <= y < N:
return True
return False
# 물복사 버그 마법
def magic():
global cloud
d = [[-1, -1], [-1, 1], [1, -1], [1, 1]] # 대각선 경우의 수 4가지
for cur_y, cur_x in cloud:
for dy, dx in d:
ny = cur_y + dy
nx = cur_x + dx
if not check(nx, ny):
continue
if graph[ny][nx] > 0:
graph[cur_y][cur_x] += 1
# 구름 생성
def create(old_cloud):
new_cloud = []
for yy in range(N):
for xx in range(N):
if graph[yy][xx] >= 2 and old_cloud[yy][xx] == 0:
new_cloud.append([yy, xx])
graph[yy][xx] -= 2
return new_cloud
cloud = [[N - 1, 0], [N - 1, 1], [N - 2, 0], [N - 2, 1]]
for m in range(M):
cloud2 = [[0] * N for _ in range(N)]
di, si = move_op[m]
di -= 1
move(di, si, cloud2)
magic()
cloud = create(cloud2)
answer = 0
for i in range(N):
for j in range(N):
answer += graph[i][j]
print(answer)
아마 최근 문제중에 유일하게 100줄이 안넘어간 코드 같다.
시간 재고 풀었을 땐 50퍼에서 틀렸습니다가 나왔다. 아마 시험장에서 10개 테케 다 통과했다고 가정하면 1.5솔 정도 됐을 것 같다.
3시간 안에 2솔은 못했다.
일단 나는 종이에 그려가면서 상어가 움직이는 경로의 좌표를 다 받아 주었다. 이게 한번만 정리하면 수식으로 간단하게 좌표가 정리가 가능하다.
그래서 이 좌표를 이용해서 줄이고 뭐하고 이렇게 했는데, 이렇게 하는 건 좋은 방법이 아닌 것 같다. 시간초과가 날 구석도 너무나도 많다.(없어진 좌표 개수만큼 for문을 돌기 때문에..)
그래서 구글링을 해보니, 이 좌표들을 1차원화 시키는 것이다.
1차원으로 만들면, 0을 줄이는 것도 간단하게 줄일 수 있고, 연속 판단은 당연히 가능하다.
상어가 블리자드 마법 시전하는 것만 2차원 -> 1차원으로 바꾸면 계속 1차원으로 풀이가 가능한, 기똥찬 풀이다.
이것을 어떻게 생각하지..?
def add_move():
cur_y = (N + 1) // 2 - 1
cur_x = (N + 1) // 2 - 1
move_list = []
for n in range(3, N + 1):
if n % 2 == 0:
continue
cur_x -= 1
move_list.append([cur_y, cur_x]) # 왼
# 아래 * N-2
for _ in range(n - 2):
cur_y += 1
move_list.append([cur_y, cur_x])
# 오 * N-1
for _ in range(n - 1):
cur_x += 1
move_list.append([cur_y, cur_x])
# 위 * N-1
for _ in range(n - 1):
cur_y -= 1
move_list.append([cur_y, cur_x])
# 왼 * N-1
for _ in range(n - 1):
cur_x -= 1
move_list.append([cur_y, cur_x])
return move_list
이렇게하면 반환값에 1번부터 순서대로 좌표가 쭉 들어있다.
moves = add_move()
blocks = []
# 1차원 배열로 바꿈
graph = [[0] * N for _ in range(N)]
# graph : 해당 좌표의 배열 번호
# blocks : 번호 담겨있는걸 일차원 배열로 나열한 것
# moves : 해당 배열 번호의 좌표
for num in range(len(moves)):
y_pos, x_pos = moves[num]
if inp[y_pos][x_pos] == 0:
blocks.append(-1)
else:
blocks.append(inp[y_pos][x_pos])
graph[y_pos][x_pos] = num
그리고 이렇게 1차원, 2차원 배열을 세팅해주어서 변환이 자유롭도록 해주었다.
# 1. 상어가 마법 시전
def magic(di, si):
global delta
global blocks
global graph
global destroy
cur_y = (N + 1) // 2 - 1 # 상어 y,x 좌표
cur_x = (N + 1) // 2 - 1
for ds in range(1, si + 1):
ny = cur_y + delta[di - 1][0] * ds
nx = cur_x + delta[di - 1][1] * ds
bubble_num = graph[ny][nx]
# -1 : 구슬 삭제
if bubble_num >= len(blocks):
continue
blocks[bubble_num] = -1
마법이 시전되는 2차원 좌표를 1차원 배열의 몇번째 인덱스인지 변환해서 없애주면 된다.
# 2. 구슬이 빈칸 채워서 이동
def move():
global blocks
temp = []
for b in blocks:
if b == -1:
continue
temp.append(b)
blocks = temp
1차원이라 정말 간단하다.
# 3. 구슬 폭발 후 재배열
def explode():
global blocks
global destroy
count = 0
bef = 0
block_num = 0
flag = False
for b in range(len(blocks)):
if blocks[b] == blocks[bef]: # 이전 블록과 연속되면
block_num = blocks[b]
count += 1
else:
if count >= 4:
flag = True
for n in range(bef, b):
blocks[n] = -1
destroy[block_num] += count
count = 1
bef = b
block_num = blocks[b]
if count >= 4:
flag = True
for n in range(bef, len(blocks)):
blocks[n] = -1
destroy[block_num] += count
return flag
구슬 폭발시키면서 폭발한 개수 카운트 해준다.
이 작업은 폭발시킬 구슬이 없을 때 까지 폭발 -> 0 땡겨서 채우기 이것을 반복해야 한다.
# 4. 구슬 변화
def trans():
global blocks
q = deque()
count = 0
bef = 0
block_num = 0
for i in range(len(blocks)):
if blocks[i] == blocks[bef]:
block_num = blocks[i]
count += 1
else:
q.append([count, block_num])
count = 1
bef = i
block_num = blocks[i]
q.append([count, block_num])
tmp = []
while q:
a, b = q.popleft()
if a != 0:
tmp.append(a)
if len(tmp) == N * N - 1:
break
if b != 0:
tmp.append(b)
if len(tmp) == N * N - 1:
break
blocks = tmp
(구슬의 개수, 구슬의 번호) 로 배열을 채워야 한다.
이 때, 그래프의 크기보다 배열의 길이가 길어지면 안되는 것을 유의해야 한다.
이렇게 하면 간단하게 풀린다. 이렇게 풀면 1시간 안걸리는듯..
from collections import deque
delta = [[-1, 0], [1, 0], [0, -1], [0, 1]] # y,x 4가지 방향 순서
N, M = map(int, input().split())
inp = []
for _ in range(N):
inp.append(list(map(int, input().split())))
magics = []
for _ in range(M):
magics.append(map(int, input().split()))
# 2차원 배열을 1차원 배열로 바꿔보자.
def add_move():
cur_y = (N + 1) // 2 - 1
cur_x = (N + 1) // 2 - 1
move_list = []
for n in range(3, N + 1):
if n % 2 == 0:
continue
cur_x -= 1
move_list.append([cur_y, cur_x]) # 왼
# 아래 * N-2
for _ in range(n - 2):
cur_y += 1
move_list.append([cur_y, cur_x])
# 오 * N-1
for _ in range(n - 1):
cur_x += 1
move_list.append([cur_y, cur_x])
# 위 * N-1
for _ in range(n - 1):
cur_y -= 1
move_list.append([cur_y, cur_x])
# 왼 * N-1
for _ in range(n - 1):
cur_x -= 1
move_list.append([cur_y, cur_x])
return move_list
moves = add_move()
blocks = []
# 1차원 배열로 바꿈
graph = [[0] * N for _ in range(N)]
# graph : 해당 좌표의 배열 번호
# blocks : 번호 담겨있는걸 일차원 배열로 나열한 것
# moves : 해당 배열 번호의 좌표
for num in range(len(moves)):
y_pos, x_pos = moves[num]
if inp[y_pos][x_pos] == 0:
blocks.append(-1)
else:
blocks.append(inp[y_pos][x_pos])
graph[y_pos][x_pos] = num
# ---- 세팅 끝 ----
destroy = [0, 0, 0, 0]
# 1. 상어가 마법 시전
def magic(di, si):
global delta
global blocks
global graph
global destroy
cur_y = (N + 1) // 2 - 1 # 상어 y,x 좌표
cur_x = (N + 1) // 2 - 1
for ds in range(1, si + 1):
ny = cur_y + delta[di - 1][0] * ds
nx = cur_x + delta[di - 1][1] * ds
bubble_num = graph[ny][nx]
# -1 : 구슬 삭제
if bubble_num >= len(blocks):
continue
blocks[bubble_num] = -1
# 2. 구슬이 빈칸 채워서 이동
def move():
global blocks
temp = []
for b in blocks:
if b == -1:
continue
temp.append(b)
blocks = temp
# 3. 구슬 폭발 후 재배열
def explode():
global blocks
global destroy
count = 0
bef = 0
block_num = 0
flag = False
for b in range(len(blocks)):
if blocks[b] == blocks[bef]: # 이전 블록과 연속되면
block_num = blocks[b]
count += 1
else:
if count >= 4:
flag = True
for n in range(bef, b):
blocks[n] = -1
destroy[block_num] += count
count = 1
bef = b
block_num = blocks[b]
if count >= 4:
flag = True
for n in range(bef, len(blocks)):
blocks[n] = -1
destroy[block_num] += count
return flag
# 4. 구슬 변화
def trans():
global blocks
q = deque()
count = 0
bef = 0
block_num = 0
for i in range(len(blocks)):
if blocks[i] == blocks[bef]:
block_num = blocks[i]
count += 1
else:
q.append([count, block_num])
count = 1
bef = i
block_num = blocks[i]
q.append([count, block_num])
tmp = []
while q:
a, b = q.popleft()
if a != 0:
tmp.append(a)
if len(tmp) == N * N - 1:
break
if b != 0:
tmp.append(b)
if len(tmp) == N * N - 1:
break
blocks = tmp
for d, s in magics:
magic(d, s)
move()
while explode():
move()
trans()
answer = destroy[1] + 2 * destroy[2] + 3 * destroy[3]
print(answer)
복기 끝!