
- 재귀 구현 시, for문 안에서 재귀함수 호출할 때 값 여러개 리턴되는데 하나의 변수에 다 할당하는 실수 주의하기
move_knight()에서 실수하지 않기
- 이미 다른 기사가 한번 밀었던 기사라면 패스(하나당 한 번만 밀어야 함)!! 면적이 w x h이므로 맞닿은 기사가 여러명일 수 있음 주의하기
from collections import defaultdict
L, N, Q = map(int, input().split())
maps = []
# 기사들의 위치를 표시할 맵
k_maps = [[0] * L for _ in range(L)]
for i in range(L):
line = list(map(int, input().split()))
maps.append(line)
positions = defaultdict(tuple)
bodys = defaultdict(tuple)
origin_hearts = defaultdict(int)
hearts = defaultdict(int)
# 죽은 기사 리스트
out_list = []
# 각 기사의 위치를 맵에 그리는 함수
def draw_body(r, c, num):
h, w = bodys[num]
for i in range(h):
for j in range(w):
if r + i >= L or c + j >= L:
print(num, '이 맵 밖을 벗어남', r + i, c + j)
return 0
k_maps[r + i][c + j] = num
def count_damage(num):
h, w = bodys[num]
r, c = positions[num]
cnt = 0
for i in range(h):
for j in range(w):
# 해당 영역에 1(함정)이 있으면 누적
if r + i >= L or c + j >= L:
print(num, '이 맵 밖을 벗어남', r + i, c + j)
return 0
if maps[r + i][c + j] == 1:
cnt += 1
return cnt
for i in range(1, N + 1): # 기사 번호: 1 ~ N 번
r, c, h, w, k = map(int, input().split())
# 0부터 시작하도록 인덱스 맞춰주기!
r -= 1
c -= 1
positions[i] = (r, c)
bodys[i] = (h, w)
hearts[i] = k
origin_hearts[i] = k
draw_body(r, c, i)
dx = [-1, 0, 1, 0]
dy = [0, 1, 0, -1]
# 기사가 이동하는 방향에 따라 고려해야 하는 면의 좌표들을 반환
def get_part(r, c, num, d):
part_list = []
h, w = bodys[num]
if d == 0:
for i in range(w):
part_list.append((r, c + i))
elif d == 1:
for i in range(h):
part_list.append((r + i, c + w - 1))
elif d == 2:
for i in range(w):
part_list.append((r + h - 1, c + i))
elif d == 3:
for i in range(h):
part_list.append((r + i, c))
return part_list
# --------------------------------------------------------------
def move_knight(n, direction):
# 밀거나 밀려서 이동하는 기사 리스트
move_knight_list = set()
move_knight_list.add(n)
def move_check(num, d):
r, c = positions[num]
parts = get_part(r, c, num, d)
for r, c in parts:
nr, nc = r + dx[d], c + dy[d]
if nr < 0 or nr >= L or nc < 0 or nc >= L:
return False
# 이동할 곳에 하나라도 벽이 있다면
if maps[nr][nc] == 2:
return False
# 이동할 곳에 다른 기사가 있다면 같은 방향으로 밀기
if k_maps[nr][nc] > 0:
another_num = k_maps[nr][nc]
# 이미 다른 기사가 한번 밀었던 기사라면 패스(하나당 한 번만 밀어야 함)!! 맞닿은 기사가 여러명일 수 있음 주의!
if another_num in move_knight_list:
continue
# 같이 밀리는 기사 리스트 추가
move_knight_list.add(another_num)
# 재귀 호출
# for문 안에 있으므로 여러개의 move_check 값이 리턴됨 주의!! 하나라도 False가 나오면 바로 return False 하기
if not move_check(another_num, d):
return False
return True
is_possible = move_check(n, direction)
# 같이 밀리는 기사 리스트와 움직임 가능 여부 리턴
return list(move_knight_list), is_possible
# --------------------------------------------------------------------
# 명령
for i in range(Q):
# move_num이 move_dir 방향으로 1칸 이동하라
move_num, move_dir = map(int, input().split())
# 사라진 기사에게 명령하면 패스
if move_num in out_list:
continue
# 1) 기사 이동
move_knight_list, is_possible = move_knight(move_num, move_dir)
# 해당 방향으로 이동이 가능하다면, 각 기사의 좌표 이동시키기
if is_possible:
for num in move_knight_list:
origin_r, origin_c = positions[num]
nr, nc = origin_r + dx[move_dir], origin_c + dy[move_dir]
positions[num] = (nr, nc)
# 이동 불가능하다면 아무일 일어나지 않음
else:
continue
# 이동 후, 밀려난 기사들 피해 누적하기
for num in move_knight_list:
# 명령받은 기사는 피해 제외
if num == move_num:
continue
damage = count_damage(num)
# 현재 체력 이상의 대미지 입을 시 사라짐
if damage >= hearts[num]:
out_list.append(num)
del hearts[num]
del positions[num]
del origin_hearts[num]
# 대미지보다 체력이 더 많은 경우
else:
hearts[num] -= damage
# 이동한 기사들 포함 생존한 모든 기사들을 k_maps 초기화 후 위치 다시 그려주기
k_maps = [[0] * L for _ in range(L)]
for key, value in positions.items():
draw_body(value[0], value[1], key)
# 살아남은 기사들의 시작 체력을 모두 더한 후, 최종 체력을 모두 더해서 빼주면 생존한 기사들의 총 데미지의 합이 됨
print(sum(origin_hearts.values()) - sum(hearts.values()))