프로그래밍/문제풀이
[Python] 백준 21611 마법사 상어와 블리자드
성수동이민기
2022. 10. 15. 15:23
https://www.acmicpc.net/problem/21611
21611번: 마법사 상어와 블리자드
마법사 상어는 파이어볼, 토네이도, 파이어스톰, 물복사버그, 비바라기 마법을 할 수 있다. 오늘 새로 배운 마법은 블리자드이고, 크기가 N×N인 격자에서 연습하려고 한다. N은 항상 홀수이고, (
www.acmicpc.net
# 21611 마법사 상어와 블리자드
# 빙글빙글 돌면서 보는 숫자들을 모아 하나의 리스트로 만들어주는 함수
def make_one():
cur_r = N // 2
cur_c = N // 2
cur_dir = 0
num = 1
while cur_c >= 0:
for _ in range(num):
cur_r += dr[cur_dir]
cur_c += dc[cur_dir]
# 범위 밖이면 백트래킹
if cur_c == -1:
return
if grounds[cur_r][cur_c] > 0:
one_list.append(grounds[cur_r][cur_c])
# 0이면 백트래킹
elif grounds[cur_r][cur_c] == 0:
return
if cur_dir % 2 == 1:
num += 1
cur_dir = (cur_dir + 1) % 4
return
# 일렬로 나열, 4개 이상 연속하면 폭파시키기
def boom():
temp_list = one_list[:]
check_again = True
pop_index = list()
while check_again:
check_again = False
result_list = list()
num = 1
if temp_list:
prev_num = temp_list[0]
for i in range(1, len(temp_list)):
if prev_num == temp_list[i]:
num += 1
else:
result_list.append([num, prev_num])
prev_num = temp_list[i]
num = 1
# 마지막은 수동으로 넣기
result_list.append([num, prev_num])
# temp_list 다시 만들기
temp_list = list()
for num, prev_num in result_list:
if num >= 4:
result[prev_num] += num
check_again = True
else:
temp_list.extend([prev_num] * num)
return temp_list
def make_group():
# 갯수 세어서 하나의 그룹으로 만들기
result_list = list()
num = 1
if one_list:
prev_num = one_list[0]
for i in range(1, len(one_list)):
if prev_num == one_list[i]:
num += 1
else:
# 지금까지 쌓은 갯수 넣기
result_list.extend([num, prev_num])
prev_num = one_list[i]
num = 1
# 마지막은 수동으로 넣기
result_list.extend([num, prev_num])
return result_list[:N ** 2]
# 좌 하 우 상
dr = [0, 1, 0, -1]
dc = [-1, 0, 1, 0]
# 0 상 하 좌 우
ddr = [0, -1, 1, 0, 0]
ddc = [0, 0, 0, -1, 1]
N, M = map(int, input().split())
grounds = [list(map(int, input().split())) for _ in range(N)]
result = [0, 0, 0, 0]
ds = list()
for _ in range(M):
di, si = map(int, input().split())
ds.append([di, si])
for idx in range(M):
di, si = ds[idx]
temp_r = N // 2
temp_c = N // 2
# 구슬 파괴
for _ in range(si):
temp_r += ddr[di]
temp_c += ddc[di]
if 0 <= temp_r < N and 0 <= temp_c < N:
grounds[temp_r][temp_c] = -1
# 순서대로 체크하기, 일렬로 만들기
one_list = list()
make_one()
# 4개 이상 연속하는 구슬 폭파
one_list = boom()
# 하나의 그룹으로 만들기
one_list = make_group()
# 칸에 순서대로 넣기
cur_r = N // 2
cur_c = N // 2
cur_dir = 0
num = 1
idx = 0
grounds = [[0] * N for _ in range(N)]
while cur_c >= 0:
for _ in range(num):
cur_r += dr[cur_dir]
cur_c += dc[cur_dir]
if cur_c == -1:
break
if idx < len(one_list):
grounds[cur_r][cur_c] = one_list[idx]
else:
cur_c = -1
break
idx += 1
if cur_dir % 2 == 1:
num += 1
cur_dir = (cur_dir + 1) % 4
ans = 0
for i in range(1, 4):
ans += result[i] * i
print(ans)
"""
제출하자마자 틀렸습니다
거의 마지막 부분 칸에 순서대로 넣을 때 다음을 추가
if cur_c == -1:
break
44% 틀렸습니다.
문제 조건대로 풀기,,
4개 이상 폭파시킬 때
폭파시키고 나서 임의로 첫 번째 idx부터 다시 탐색함
시키는대로 4개 이상 이어지는 부분 다 찾고나서 터뜨린 뒤에
다시 처음부터 탐색해야 한다.
"""
예전 풀이
# 21611 마법사 상어와 블리자드
# 하 우 상 좌
dr = (0, 1, 0, -1, 0)
dc = (0, 0, 1, 0, -1)
# 0, 상, 하, 좌, 우
bli_rc = [(0, 0), (-1, 0), (1, 0), (0, -1), (0, 1)]
# 일렬 리스트 만들기
def make_one_list():
current_r = (N // 2) + 1
current_c = (N // 2) + 1
move_num = 2
while current_c > 0:
for i in range(1, 5):
for j in range(move_num):
# 처음엔 왼쪽으로 이동
if i == 1 and j == 0:
current_c -= 1
else:
current_r += dr[i]
current_c += dc[i]
one_list.append(blocks[current_r][current_c])
move_num += 2
if blocks[current_r][current_c + dc[i]] == 0:
break
return
# blizrd 마법 이후 블록 재배치
def make_blocks():
for i in range(N + 1):
for j in range(N + 1):
blocks[i][j] = 0
current_r = (N // 2) + 1
current_c = (N // 2) + 1
move_num = 2
one_list_idx = 0
length = len(one_list)
while current_c > 0:
for i in range(1, 5):
for j in range(move_num):
# 처음엔 왼쪽으로 이동
if i == 1 and j == 0:
current_c -= 1
else:
current_r += dr[i]
current_c += dc[i]
if length <= one_list_idx:
return
while one_list[one_list_idx] == 0:
one_list_idx += 1
if length <= one_list_idx:
return
blocks[current_r][current_c] = one_list[one_list_idx]
one_list_idx += 1
move_num += 2
return
# 4개 연속이면 터뜨리기
def pop_one_list():
global one_list
new_one_list = list()
bomb_check = 0
cnt = 1
temp = -1
for i in range(len(one_list)):
if one_list[i] != temp:
temp = one_list[i]
if cnt >= 4:
bomb_check = 1
bomb_num[new_one_list[-1]] += cnt
for _ in range(cnt):
new_one_list.pop()
cnt = 0
new_one_list.append(one_list[i])
cnt += 1
if cnt >= 4:
bomb_check = 1
bomb_num[new_one_list[-1]] += cnt
for _ in range(cnt):
new_one_list.pop()
one_list = new_one_list[:]
return bomb_check
# 구슬 변화(갯수, 번호)
def evolution():
global blocks
current_r = (N // 2) + 1
current_c = (N // 2) + 1
move_num = 2
cnt = 0
bf_num = blocks[current_r][current_c - 1]
flag = 0
while current_c > 0:
for i in range(1, 5):
for j in range(move_num):
# 처음엔 왼쪽으로 이동
if i == 1 and j == 0:
current_c -= 1
else:
current_r += dr[i]
current_c += dc[i]
if current_c <= 0 or current_r > N or blocks[current_r][current_c] == 0:
flag = 1
break
# 전후 숫자가 달라지면 visited 초기화 가능
if blocks[current_r][current_c] == bf_num:
cnt += 1
elif blocks[current_r][current_c] != 0:
evo_list.append(cnt)
evo_list.append(bf_num)
cnt = 1
bf_num = blocks[current_r][current_c]
else:
break
if flag == 1:
break
if flag == 1:
break
move_num += 2
evo_list.append(cnt)
evo_list.append(bf_num)
# 블록 재배치
new_blocks = [[0] * (N + 1) for _ in range(N + 1)]
current_r = (N // 2) + 1
current_c = (N // 2) + 1
move_num = 2
evo_list_idx = 0
length = len(evo_list)
while current_c > 0:
for i in range(1, 5):
for j in range(move_num):
# 처음엔 왼쪽으로 이동
if i == 1 and j == 0:
current_c -= 1
else:
current_r += dr[i]
current_c += dc[i]
if length <= evo_list_idx:
break
if current_r <= 0 or current_c <= 0:
blocks = new_blocks[:]
return
new_blocks[current_r][current_c] = evo_list[evo_list_idx]
evo_list_idx += 1
move_num += 2
blocks = new_blocks[:]
return
N, M = map(int, input().split())
blocks = [[0] * (N + 1)] + [[0] + list(map(int, input().split())) for _ in range(N)]
one_list = list()
move_list = list()
bomb_num = [0, 0, 0, 0]
for _ in range(M):
temp_r, temp_c = map(int, input().split())
move_list.append([temp_r, temp_c])
for blizard_d, blizard_s in move_list:
# blizard 마법
current_r = (N // 2) + 1
current_c = (N // 2) + 1
for _ in range(blizard_s):
current_r += bli_rc[blizard_d][0]
current_c += bli_rc[blizard_d][1]
blocks[current_r][current_c] = 0
one_list = list()
# blizard 마법 이후 일렬 리스트로 붙이기
make_one_list()
# blizard 마법 이후 블록 재배치
make_blocks()
one_list = list()
# 일렬 리스트 만들기
make_one_list()
again = 1
while again:
# 4개 연속이면 터뜨리기
again = pop_one_list()
# 블록 재배치
make_blocks()
evo_list = list()
# 구슬 변화(갯수, 번호)
evolution()
ans = 0
for i in range(1, 4):
ans += bomb_num[i] * i
print(ans)