코드를 잘 짰는지는 모르겠지만, 너무 많이 틀렸어서 틀린 부분과 풀이 방법을 기록하기 위해 이 글을 쓴다. ("내가 다 해줬잖아..왜 안돼.."를 가장 많이 외치게 했던 문제였다..)
문제 내용: https://www.acmicpc.net/problem/3190
import sys
import itertools
from collections import deque
time = 0
snake_pos = deque([(1, 1)]) # 꼬리가 맨 앞, 머리가 맨 뒤
live = True
directions = ["up", "right", "down", "left"]
dir_index = 1
def turn_snake(dir):
global dir_index
# 방향 바꾸기
if dir == "L":
dir_index = (dir_index - 1) % 4
if dir == "D":
dir_index = (dir_index + 1) % 4
def move_snake(dir):
global live
global snake_pos
head_pos = snake_pos[-1]
if dir == "right":
snake_pos.append((head_pos[0],head_pos[1] + 1))
elif dir == "left":
snake_pos.append((head_pos[0],head_pos[1] - 1))
elif dir == "up":
snake_pos.append((head_pos[0] - 1,head_pos[1]))
elif dir == "down":
snake_pos.append((head_pos[0] + 1,head_pos[1]))
board_size = int(sys.stdin.readline().strip())
apple_num = int(sys.stdin.readline().strip())
apple_positions = []
for i in range(apple_num):
apple_position = sys.stdin.readline().strip()
row = int(apple_position.split()[0])
col = int(apple_position.split()[1])
apple_positions.append((row, col))
turn_num = int(sys.stdin.readline().strip())
turn_times = []
dirs = []
for i in range(turn_num):
turn = sys.stdin.readline().strip()
turn_times.append(int(turn.split()[0]))
dirs.append(turn.split()[1])
while live == True:
time += 1
# 이동
move_snake(directions[dir_index])
# 생사 확인
if (snake_pos[-1][0] < 1) or (snake_pos[-1][0] > board_size) or (snake_pos[-1][1] < 1) or (snake_pos[-1][1] > board_size):
live = False
if len(snake_pos) > 1 and snake_pos[-1] in deque(itertools.islice(snake_pos, 0, len(snake_pos)-1)):
live = False
# 회전
if time in turn_times:
time_index = turn_times.index(time)
turn_snake(dirs[time_index])
# 꼬리 없애기 or 사과 먹기
if snake_pos[-1] not in apple_positions:
snake_pos.popleft()
else:
apple_positions.remove(snake_pos[-1])
print(time)
반례모음: https://www.acmicpc.net/board/view/56469
이 이유로 틀린 사람들이 많은 것 같다. 처음에는 없애지 않아도 되지 않나 싶었는데, 먹은 사과를 또 먹을 수도 있기 때문에 없애주어야 한다.
사과를 제거하지 않으면, 반례모음의 5번 케이스에서 다른 답이 나온다. 이미 먹은 사과를 또 먹어서 길이가 길어졌기 때문에, 자기 자신의 꼬리와 만나 죽게 된다.
이동한 후에 바로 죽었는지 확인을 해야하는데, 머리 이동하고 사과가 없으면 꼬리를 없앤 후에 생사 확인을 했다. 머리가 꼬리를 만나면 죽어야 하는데, 그 경우를 다루지 못해 다른 결과가 나왔다. 그래서 아래와 같은 순서로 코드를 변경해주었다.
전: 시간 추가 → 이동 → 방향 바꾸기(이동x) → 꼬리 없애기 or 사과먹기 → 생사 확인
후: 시간 추가 → 이동 → 생사 확인 → 방향 바꾸기(이동x) → 꼬리 없애기 or 사과먹기
처음에 코드를 짤 때 아래 head_pos와 tail_pos처럼 머리와 꼬리의 위치를 변수에 할당한 후 생사 확인을 할 때 사용했다.
그런데 머리를 늘려서 이동한 후 머리와 꼬리 위치를 할당하였기 때문에, 그 후에 꼬리를 자르게 되었을 때를 반영하지 않는다. 따라서 뱀의 꼬리가 실제 위치와 달라지는 문제가 발생하여, 원래 죽어야하는데 안죽는다거나 죽어야하는데 안죽는 그런 경우가 생긴다.
while live == True:
time += 1
# 이동
move_snake(directions[dir_index])
# 머리와 꼬리 위치
head_pos = snake_pos[-1]
tail_pos = snake_pos[0]
# 회전
if time in turn_times:
time_index = turn_times.index(time)
turn_snake(dirs[time_index])
# 사과 못 먹으면 꼬리 자르기
if head_pos not in apple_positions:
snake_pos.popleft()
# 생사 확인
if (head_pos[0] < 1) or (head_pos[0] > board_size) or (head_pos[1] < 1) or (head_pos[1] > board_size):
live = False
if len(snake_pos) > 1 and head_pos == tail_pos:
live = False
뱀은 벽 또는 자기 자신의 몸과 부딪히면 죽는다. 그런데 처음에 잘못 이해하고, 꼬리를 만날 때만 죽게 했더니 틀렸다.
전: if len(snake_pos) > 1 and head_pos == tail_pos:
live = False
후: if len(snake_pos) > 1 and snake_pos[-1] in deque(itertools.islice(snake_pos, 0, len(snake_pos)-1)):
live = False
그래서 몸통의 위치를 모아놓은 배열안에 머리의 위치가 있다면 죽도록 변경했다.
(deque에서 slice가 필요한 경우 itertools.isslice를 사용한다.)
다른 해결 방법들을 보니 맞는 조건일때만 패스하고, 그 외(else)는 죽게 했던데, 그 방법이 좋은 것 같다. 죽는 조건을 적는 게 더 까다로운 것 같아서 살아있을 때에만 패스하는 방법으로 변경해봐야겠다.