[백준] 16236_아기 상어 (BFS 이용)
📕 문제
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. 최단거리를 찾는 문제이기 때문에 bfs를 이용해서 풀었다.
2. 보통 bfs 최단거리 찾기 문제는 시작점과 도착점을 알려주고 최단거리를 물어보는 문제가 많다. 하지만 아기상어 문제는 계속해서 먹을수 있는 물고기를 찾아다니며 매 경우마다 최단거리를 구해줘야 하기 때문에 bfs가 여러번 돌아가게 구현해야 한다.
3. 먼저 시작점(9)를 찾아주고 bfs를 시작한다.
4. 하지만 시작하기에 앞서 먼저 공간 안에 먹을 수 있는 물고기가 있는지부터 확인해야한다. 만약 없다면 바로 bfs를 중지시킨다.
5. 있다면 사방으로 확인하며 갈수 있는 방향을 찾아야한다.
6. 동시에 먹을 수 있는 물고기를 발견하면 그 때의 최단거리를 따로 변수에 담아둔다.
7. 그 이유는 같은 거리에 또 다른 물고기가 있을 수 있기 때문에 모든 물고기를 일단 다 찾아서 물고기의 위치를 fish라는 2차 행렬에 표시해 둔다.
8. 문제에서 보면 가장 위쪽 가장 왼쪽부터 먹어야 한다고 했기 때문에 2중 for문을 이용해서 fish행렬을 돌면서 가장 먼저 발견한 물고기를 먹고 먹은 물고기의 위치에 해당하는 공간에서의 위치는 0으로 바꿔준다. 그리고 그 위치와 최단거리를 반환한다.
9. 위치를 반환하는 이유는 이제 그 다음으로 돌 bfs의 시작점이 그 먹은 물고기의 위치가 되기 때문이다!
10. while문을 돌때마다 cnt(먹은 물고기)를 1씩 추가해준다. 한번 돌았단 뜻은 물고기를 한마리 먹었단 뜻이기 때문이다.
11. 아기 상어는 자신의 크기만큼 물고기를 먹으면 1씩 커지기 때문에 cnt가 상어의 크기(shark)와 같아지면 shark를 +1 해주고 cnt는 다시 0으로 리셋해준다.
12. 그렇게 계속 돌면서 start가 0이 되면 더이상 공간에 먹을 물고기가 없다는 뜻이므로 bfs를 완전히 끝낸다.
from collections import deque
def bfs(start, shark):
global N
t = 0
flag1 = False
for i in range(N): # 시작하기 전 공간에 먹을 수 있는 물고기가 있는지 확인
for j in range(N):
if 0 < sea[i][j] < shark:
flag1 = True
break
if flag1:
break
if not flag1:
return 0, 0
dxy = [(-1,0),(0,-1),(0,1),(1,0)]
visited = [[0 for _ in range(N)] for _ in range(N)]
fish = [[0 for _ in range(N)] for _ in range(N)]
x, y = start
q = deque([(x, y)])
sea[x][y] = 0
flag2 = flag3 = False
while q:
x, y = q.popleft()
for dx, dy in dxy:
nx, ny = x + dx, y + dy
if -1 < nx < N and -1 < ny < N and sea[nx][ny] <= shark and visited[nx][ny] == 0:
visited[nx][ny] = visited[x][y] +1
if flag2 and visited[nx][ny] > distance:
flag3 = True
break
q.append((nx, ny))
if 0 < sea[nx][ny] < shark:
distance = visited[nx][ny]
fish[nx][ny] = 1
flag2 = True
if flag3:
break
if flag2:
for i in range(N):
for j in range(N):
if fish[i][j]:
sea[i][j] = 0
return (i,j), visited[i][j]
return 0, t
N = int(input())
sea = [list(map(int, input().split())) for _ in range(N)]
for i in range(N):
if 9 in sea[i]:
x, y = i, sea[i].index(9)
start = (x, y)
time = 0
shark = 2
cnt = 0
while start:
start, t = bfs(start, shark)
time += t
cnt += 1
if shark == cnt:
shark += 1
cnt = 0
print(time)
-> 코드가 조금 지저분한 느낌이 든다. flag를 세개나 쓰다니..!!!!! 휴 그래도 풀었으니 만족하자 😉