아기 상어
2 초 | 512 MB | 65536 | 30426 | 18462 | 43.007% |
문제
N×N 크기의 공간에 물고기 M마리와 아기 상어 1마리가 있다. 공간은 1×1 크기의 정사각형 칸으로 나누어져 있다. 한 칸에는 물고기가 최대 1마리 존재한다.
아기 상어와 물고기는 모두 크기를 가지고 있고, 이 크기는 자연수이다. 가장 처음에 아기 상어의 크기는 2이고, 아기 상어는 1초에 상하좌우로 인접한 한 칸씩 이동한다.
아기 상어는 자신의 크기보다 큰 물고기가 있는 칸은 지나갈 수 없고, 나머지 칸은 모두 지나갈 수 있다. 아기 상어는 자신의 크기보다 작은 물고기만 먹을 수 있다. 따라서, 크기가 같은 물고기는 먹을 수 없지만, 그 물고기가 있는 칸은 지나갈 수 있다.
아기 상어가 어디로 이동할지 결정하는 방법은 아래와 같다.
- 더 이상 먹을 수 있는 물고기가 공간에 없다면 아기 상어는 엄마 상어에게 도움을 요청한다.
- 먹을 수 있는 물고기가 1마리라면, 그 물고기를 먹으러 간다.
- 먹을 수 있는 물고기가 1마리보다 많다면, 거리가 가장 가까운 물고기를 먹으러 간다.
- 거리는 아기 상어가 있는 칸에서 물고기가 있는 칸으로 이동할 때, 지나야하는 칸의 개수의 최솟값이다.
- 거리가 가까운 물고기가 많다면, 가장 위에 있는 물고기, 그러한 물고기가 여러마리라면, 가장 왼쪽에 있는 물고기를 먹는다.
아기 상어의 이동은 1초 걸리고, 물고기를 먹는데 걸리는 시간은 없다고 가정한다. 즉, 아기 상어가 먹을 수 있는 물고기가 있는 칸으로 이동했다면, 이동과 동시에 물고기를 먹는다. 물고기를 먹으면, 그 칸은 빈 칸이 된다.
아기 상어는 자신의 크기와 같은 수의 물고기를 먹을 때 마다 크기가 1 증가한다. 예를 들어, 크기가 2인 아기 상어는 물고기를 2마리 먹으면 크기가 3이 된다.
공간의 상태가 주어졌을 때, 아기 상어가 몇 초 동안 엄마 상어에게 도움을 요청하지 않고 물고기를 잡아먹을 수 있는지 구하는 프로그램을 작성하시오.
입력
첫째 줄에 공간의 크기 N(2 ≤ N ≤ 20)이 주어진다.
둘째 줄부터 N개의 줄에 공간의 상태가 주어진다. 공간의 상태는 0, 1, 2, 3, 4, 5, 6, 9로 이루어져 있고, 아래와 같은 의미를 가진다.
- 0: 빈 칸
- 1, 2, 3, 4, 5, 6: 칸에 있는 물고기의 크기
- 9: 아기 상어의 위치
아기 상어는 공간에 한 마리 있다.
출력
첫째 줄에 아기 상어가 엄마 상어에게 도움을 요청하지 않고 물고기를 잡아먹을 수 있는 시간을 출력한다.
예제 입력 1
3
0 0 0
0 0 0
0 9 0
예제 출력 1
0
예제 입력 2
3
0 0 1
0 0 0
0 9 0
예제 출력 2
3
예제 입력 3
4
4 3 2 1
0 0 0 0
0 0 9 0
1 2 3 4
예제 출력 3
14
예제 입력 4
6
5 4 3 2 3 4
4 3 2 3 4 5
3 2 9 5 6 6
2 1 2 3 4 5
3 2 1 6 5 4
6 6 6 6 6 6
예제 출력 4
60
예제 입력 5
6
6 0 6 0 6 1
0 0 0 0 0 2
2 3 4 5 6 6
0 0 0 0 0 2
0 2 0 0 0 0
3 9 3 0 0 1
예제 출력 5
48
예제 입력 6
6
1 1 1 1 1 1
2 2 6 2 2 3
2 2 5 2 2 3
2 2 2 4 6 3
0 0 0 0 0 6
0 0 0 0 0 9
예제 출력 6
39
기존의 bfs에 구현과 시뮬레이션 기법이 들어간 문제이다.
처음에는 그동안 풀어왔던 bfs문제들처럼 접근하느라 많은 고민을 해봤지만 풀이방법을 모르겠어서 다른 분의 아이디어를 채용하여 풀었다.
사실 문제를 순서대로 해결하려 들지 않은것이 패인이었던것 같다.
문제를 차근차근 읽고 하나씩 조건을 추가해가면 풀이방법을 더 빠르게 생각할수 있을것같다.
먼저 아기상어의 위치로부터 문제의 조건에 맞춰 아기상어가 먹을수 있는 물고기들의 좌표를 파악한다.
이 파악하는 과정은 아기상어의 현재 위치에서 bfs를 실시하여 이동가능한 경로에서 먹을수 있는 물고기들의 거리를 배열에 삽입한뒤
bfs가 종료된 이후 해당 배열을 문제의 조건에 맞춰 거리순, 행 좌표순, 열 좌표순으로 재정렬하여 리턴한다.
먹을수 있는 물고기가 없을때까지 반복하게 되므로 기본 틀은 무한 반복문에서 아기 상어가 먹을수 있는 물고기의 좌표를 앞서 말한 bfs로 구하고, 배열을 리턴받는다. 배열의 길이가 0이라면 먹을수 있는 물고기가 없다는 뜻이되므로 반복문을 종료하고, 그렇지 않다면 배열의 가장 마지막 인덱스의 값을 받아와서 그 값으로 아기상어의 물고기를 이동시키고 물고기를 한마리 먹었다는 의미로 임의의 변수를 증가시킨다.
위 과정에서 주의해야 할 점은, 물고기를 먹음으로서 물고기의 크기에 변화가 생기고, 좌표의 변화가 생기기때문에 반복문을 통해서 배열의 모든 값을 하나하나 탐색하는것이 아니라 한 좌표로만 이동한후 다시 bfs를 진행해야 한다는 점이다.
또한 물고기를 한마리 섭취한 후에는 크기의 변화가 발생하는지 조건문을 통해서 확인하고, 값을 갱신해주면 된다.
위 과정을 코드로 옮기면 아래와 같다.
# https://www.acmicpc.net/problem/16236
# 아기상어의 초기 위치는 9로 주어진다.
from collections import deque
n = int(input())
# 물고기 정보 입력받기
graph = [list(map(int, input().split())) for _ in range(n)]
# 아기상어의 초기위치와 크기 지정
x, y, size = 0, 0, 2
for i in range(n):
for j in range(n):
# 아기상어의 초기위치 발견시
if graph[i][j] == 9:
x, y = i, j # 초기위치 지정
graph[i][j] = 0 # 빈공간으로 지정(아기상어가 움직이기 시작할것이므로)
# 방향벡터 정의
dx = [0, 0, -1, 1]
dy = [-1, 1, 0, 0]
# 먹을수 있는 물고기를 탐색하여 거리를 측정하는 함수(bfs)
def search(x, y): # 현재 아기상어의 위치를 대상으로 먹을수 있는 물고기의 위치를 파악하여 리턴
# bfs를 통해 현재 위치에서 먹을수 있는 물고기 위치 탐색
fish_position = [] # 물고기의 위치와 크기를 저장
queue = deque()
queue.append([x, y])
# 물고기의 이동 정보를 저장할 배열
visited = [[-1]*n for _ in range(n)]
visited[x][y] = 0 # 현재 위치 방문 처리 => 거리 누적
while queue:
# 아기상어의 현재 위치를 팝
cur_x, cur_y = queue.popleft()
# 네가지 방향에 대해서 bfs
for i in range(4):
nx = cur_x + dx[i]
ny = cur_y + dy[i]
# 영역을 벗어나지 않으면서 한번도 방문하지 않은 좌표에 대하여
if 0 <= nx < n and 0 <= ny < n and visited[nx][ny] == -1:
# 해당 좌표의 물고기의 크기가 size보다 작거나 같다면(이동 가능하다면)
if graph[nx][ny] <= size:
# 거리를 누적하며 이동
visited[nx][ny] = visited[cur_x][cur_y] + 1
# 큐에 좌표 삽입
queue.append([nx, ny])
# 먹을 수 있는 물고기(크기가 0이 아니면서 자기 크기보다 작아야함)라면 배열에 좌표와 해당 물고기까지의 거리 삽입
if 0 < graph[nx][ny] < size:
fish_position.append([nx, ny, visited[nx][ny]])
# 먹을수 있는 물고기 탐색이 끝난후
# 배열을 거리순, 위에있는 물고기순, 왼쪽에 있는 물고기 순으로 정렬하여 리턴
# 2번째 인자:거리, 0번째 인자:배열의 행 정보, 1번째 인자: 배열의 열 정보
return sorted(fish_position, key=lambda x: (-x[2], -x[0], -x[1]))
time = 0 # 아기상어가 엄마상어 도움없이 잡아먹을수 있는 시간
bite = 0 # 현재 탐색에서 먹은 물고기의 수
while True:
# 아기상어의 현재 위치를 기준으로 먹을수 있는 물고기들의 좌표 탐색
fish_position = search(x, y)
# 만약 먹을수 있는 물고기가 없다면 무한반복 탈출
if len(fish_position) == 0:
break
# 먹을수 있는 물고기중 가장 가까운 거리의 물고기로 이동
nx, ny, distance = fish_position.pop()
# 반복문을 사용하여 배열의 모든값을 탐색하지 않는 이유는,
# 물고기를 섭취함으로써 가장 가까운 거리의 물고기가 변경되기때문에 다시 search를 해야하기 때문이다.
# 물고기 잡아먹기
graph[nx][ny] = 0 # 잡아먹었다는 의미로 공간을 비우고
x, y = nx, ny # 물고기의 현재 좌표를 변경시킨다.
# 물고기를 잡아먹으러 이동하는 거리가 즉 시간이므로 시간을 누적시킨다
time += distance
bite += 1 # 물고기 1마리를 잡아먹었다는 의미로 1을 더해주고
# 잡아먹은 물고기의 수가 아기상어의 크기와 같다면 아기상어의 크기를 증가시킨다.
if bite == size:
size += 1
bite = 0 # 다시 0으로 초기화(크기가 커진 이후부터)
print(time)
'PS > BOJ' 카테고리의 다른 글
[백준 1707번] 이분 그래프 파이썬 풀이 (0) | 2024.02.08 |
---|---|
[백준10157번] 자리배정 파이썬 풀이 (0) | 2024.02.08 |
[백준 1436번] 영화감독 숌 파이썬 풀이 (0) | 2024.01.19 |
[백준 17298번] 오큰수 파이썬 풀이 (0) | 2024.01.19 |
[백준 13565번]침투 파이썬 풀이 (1) | 2024.01.19 |