문제 분석
- 가로 m, 세로 n
- 잘 익은 토마토 & 안 익은 토마토
- 보관 후 하루가 지나면 익은 토마토들의 인접한 곳에 있는 익지 않은 토마토들이 익음
- 인접 = 상하좌우
- 토마토가 혼자 익는 경우는 x
- 며칠이 지나면 다 익게 되는지 최소 일수를 알고 싶다.
- 1 : 익은 토마토
- 0 : 익지 않은 토마토
- -1 : 빈 칸
- 저장될 때 부터 모든 토마토가 익어있으면 0 리턴
- 모두 익지 못하는 상태이면 -1 리턴
코드 설계
- 넓이 우선 탐색 (bfs)
- 박스 안에 0이 있지 않으면 return
- 종료 조건 1
- 모든 토마토가 익었을 때
if 0 not in box- 박스에
- 종료 조건 2
- 모두 익지 못하는 상태
- 모든 1 주변에 0이 없는 경우
- 1을 싹다 찾고
- 상하좌우에 0이 있는지 확인
- 주변에 0이 있는 1 중 주변의 0이 1로 바뀌면
- 동시 다발적으로 하루가 지나면 주변이 싹 익음
- 이거를 박스 안에 0이 없을때 까지 반복
- bfs를 어떻게 이용할 것인가
- 1을 다 찾고
- 1의 개수만큼 반복문을 돌아서
- 1 주변의 0을 다 1로 바꾸면 다시 1 찾기
- 만약 이 중 1의 주변(상하좌우)에 0이 없으면 -1 출력하고 끝
정답 코드
1차 - 시간 초과
import sys
input = sys.stdin.readline
from collections import deque
m, n = map(int, input().split()) # m : 가로, n : 세로
box = []
for _ in range(n):
box.append(list(map(int, input().split())))
day = 0
d = deque()
dh = [-1, 1, 0, 0]
dw = [0, 0, -1, 1]
'''
def isRipe(b) :
for i in range(n) :
for j in range(m) :
if b[i][j] == 0 :
return False
return True
'''
def isRipe(b) :
for t in b :
if 0 in t :
return False
return True
def bfs(dq): # 1주변의 0 찾기
h, w = dq.popleft()
check = 0 # 1주변에 0이 있는지 확인
for k in range(4):
nh = h + dh[k]
nw = w + dw[k]
if nh < 0 or nh >= n or nw < 0 or nw >= m:
continue
if box[nh][nw] != 0:
continue
check += 1
box[nh][nw] = 1
if check == 0 : # 만약 1 주변에 0이 없으면 0 리턴
return 0
return 1
while True:
# 종료조건1 : 모든 토마토가 익었을 때
if isRipe(box):
print(day)
break
# 종료조건2 : 모두 익지 못하는 상태
# 1을 찾으면 bfs()에 넣어서 상하좌우 탐색
ripe_tomato = [] # 새로 1 찾기
for i in range(n):
for j in range(m):
if box[i][j] == 1:
ripe_tomato.append((i, j))
check = 0
for t in ripe_tomato:
d.append(t)
check += bfs(d) # 상하좌우 탐색해서 1로 만들기
if check == 0 :
print(-1)
break
day += 1 # 1 주변 0을 다 1로 바꾸면 하루가 지남
이걸 bfs를 했다고 할 수 있나..?
다른 사람 코드 1
import sys
from collections import deque
input = sys.stdin.readline
m, n = map(int, input().split())
box = []
dh = [-1, 1, 0, 0]
dw = [0, 0, -1, 1]
for _ in range(n) :
box.append(list(map(int, input().split())))
d = deque()
for i in range(n) :
for j in range(m) :
if box[i][j] == 1 :
d.append((i,j))
while d :
for _ in range(len(d)) : # 큐에 삽입된 익은 토마토의 수 만틈
h, w = d.popleft()
# 익은 토마토의 인접한 토미트들과 비교
for i in range(4) :
nh = h + dh[i]
nw = w + dw[i]
if nh<0 or nh>=n or nw<0 or nw>m :
continue
if box[nh][nw] != 0 :
continue
# box[nh][nw] = 1
box[nh][nw] = box[h][w] + 1
# 익은 토마토를 다시 큐에 삽입
d.append((nh, nw))
day = 0
for row in range(n) :
for tomato in row :
if tomato == 0 : # bfs를 다 돌았는데도 토마토가 다 익지 않은 경우
print(-1)
exit(0)
day = max(day, max(row)) # 토마토 중 가장 큰 값 (소요일) 찾기
print(day - 1) # 익은 토마토의 초기값이 1이기 때문에 -1
다른 사람 코드 2
from collections import deque
M, N = map(int, input().split())
arr = []
queue = deque()
for i in range(N):
row = list(map(int, input().split()))
arr.append(row)
for j in range(M):
if row[j] == 1: #익은 토마토 덱에 위치 넣기
queue.append((i, j))
d = d=[(-1,0),(1,0),(0,-1),(0,1)] #상하좌우
def bfs():
while queue: #토마토 다 익힐때까지 반복 (0없애기)
x, y = queue.popleft()
for i in range(4):
nx = x + d[i][0]
ny = y + d[i][1]
if 0 <= nx < N and 0 <= ny < M and arr[nx][ny] == 0: #다음 위치가 안익은 토마토이면 익혀나가기
arr[nx][ny] = arr[x][y] + 1
queue.append((nx, ny)) #이것도 익은거니까 덱에 위치 넣기
bfs()
def get_day():
result = 0 #결과
for row in arr:
if 0 in row: #안익은토마토 하나라도 있으면 -1
return -1
row_max = max(row)
if result < row_max:
result = row_max
return result - 1 #며칠 걸리냐 했으니까 -1 빼야 함
print(get_day())
출처: https://choihjhj.tistory.com/entry/백준파이썬-7576번-토마토 [개발 블로그:티스토리]
2회차 - 25.03.11
- 익지 않은 토마토는 주변의 익은 토마토의 영향을 받아 익는다.
- 주변 : 상하좌우
- 창고에 보관된 토마토들이 며칠이 지나면 다 익는지 알고 싶다. (최소 일수)
- n, m : 상자의 크기
- 토마토 정보
- 0 : 안익은 토마토
- 1 : 익은 토마토
- -1 : 토마토가 들어있지 않은 칸
- 모든 토마토가 익어있는 상태라면 0
- 토마토가 모두 익지 못하는 상황이면 -1
- 경우 1. 모든 토마토가 익은 상태 : 1과 -1만 존재
- 경우 2. 다 익지 않은 상태
- 경우 2-1. 토마토가 모두 익지 못하는 상황
- -1로 둘러 쌓인 0
- 0 상하좌우 -1만 있다면, print(0)
- 경우 2-2. 익은 것들이 안익은 거를 익게 만듦
- 경우 2-1. 토마토가 모두 익지 못하는 상황
- 최소 날짜 → 익은 토마토들이 동시 다발적으로 주변의 익지 않은 토마토를 익게 함
- 우선 익은 토마토를 찾기
- 익은 토마토들 상하좌우 주변 안익은 토마토 익게 하기
- 익은 토마토 전체 찾기를 1번 할 때마다 하루가 지남
- 언제까지? 모든 익을 수 있는 토마토가 익을 때까지 (0이 없을 때까지)
이전에 토마토가 모두 익지 못하는 상황은 거름
1차 : 완탐 - 시간 초과
import sys
input = sys.stdin.readline
from collections import deque
n,m = map(int, input().split())
g = [list(map(int, input().split())) for _ in range(m)]
dh = [-1, 1, 0, 0]
dw = [0, 0, -1, 1]
def cantRipe(h, w) :
for i in range(4):
nh = h + dh[i]
nw = w + dw[i]
if nh < 0 or nh >= m or nw < 0 or nw >= n:
continue
if g[nh][nw] != -1:
return False
return True # 상하좌우가 -1로 막혔으면
def allRipe(g_tomato) : # 다 익음
all_ripe = 0
for i in range(m):
if not 0 in set(g_tomato[i]):
all_ripe += 1
if all_ripe == m:
return True
return False
def makeRipe(h,w) :
global g
for i in range(4) :
nh = h + dh[i]
nw = w + dw[i]
if nh < 0 or nh >= m or nw < 0 or nw >= n :
continue
if g[nh][nw] == 0 :
g[nh][nw] = 1
# 1. 다 익어 있는경우 : 1과 -1만 있는 경우
if allRipe(g) :
print(0)
exit(0)
# 2. 다 익을 수 없는 경우
for r in range(m):
for c in range(n) :
if g[r][c] == 0 :
if cantRipe(r,c) :
print(-1)
exit(0)
day = 0
# 3. 모든 토마토가 익을 수 있는 경우
while not allRipe(g) :
d_tomatoes = deque() # 익은 토마토 담기
for r in range(m):
for c in range(n) :
if g[r][c] == 1 :
d_tomatoes.append((r,c))
while d_tomatoes : # 주변 토마토 익히기
h,w = d_tomatoes.popleft()
makeRipe(h,w)
# 주변 토마토 다 익게 만들면
day += 1
print(day)
매번 익은 토마토를 찾으므로, 이미 익은걸 확인하고 주변을 익게 만든 토마토도 다시 체크하므로 코드가 비효율적이다.
익은 토마토를 다 bfs에 넣으면 너비 우선 탐색이기 때문에 한번 주변을 탐색할 때마다 하루가 지남
2차
import sys
from collections import deque
input = sys.stdin.readline
n, m = map(int, input().split())
g = [list(map(int, input().split())) for _ in range(m)]
dh = [-1, 1, 0, 0]
dw = [0, 0, -1, 1]
d = deque() # 익은 토마토 담기
for r in range(m) :
for c in range(n) :
if g[r][c] == 1 :
d.append((r,c))
def bfs() :
days = -1 # 첫날 -1
while d :
for _ in range(len(d)) :
h,w = d.popleft()
for i in range(4) :
nh = h + dh[i]
nw = w + dw[i]
if 0 <= nh < m and 0 <= nw < n and g[nh][nw] == 0: # 범위안에 있다면
g[nh][nw] = 1 # 주변 토마토 익고
d.append((nh, nw)) # 새로 익은 토마토 넣기
days += 1
for row in g :
if 0 in row :
return -1
return days
print(bfs())
3회차 - 25.03.14
import sys
from collections import deque
input = sys.stdin.readline
m, n = map(int, input().split())
box = [list(map(int, input().split())) for _ in range(n)]
dh = [-1, 1, 0, 0]
dw = [0, 0, -1, 1]
d = deque()
# 이미 다 익어있다면 day = 0
# check = 0
# for i in range(n) :
# if 0 not in box[i] :
# check += 1
# if check == n :
# print(0)
# sys.exit(0)
# 우선 토마토 싹 다 넣기
for i in range(n) :
for j in range(m) :
if box[i][j] == 1 :
d.append((i, j))
def dfs() :
day = -1
'''
왜 day = -1로 시작?
익은 토마토를 모두 덱에 넣어둔 상태에서 시작
-> 첫번째 반복에서는 주변의 익지 않은 토마토에 대한 너비우선탐색을 하므로
아무 변화도 없이 기존 익은 토마토만 처리
'''
while d :
for _ in range(len(d)) :
h, w = d.popleft() # 1 토마토를 기준으로
# 상하좌우 안익은 토마토 덱에 넣기
for k in range(4) :
nh = h + dh[k]
nw = w + dw[k]
if (0<=nh<n and 0<=nw<m) and box[nh][nw] == 0 :
box[nh][nw] = 1
d.append((nh, nw))
day += 1
# 처음부터 다 익었다면 새로 append되지 않고 그냥 day += 1
# 따라서 L13~20 부분을 따로 구현하지 않아도 된다.
# 나왔는데 다 익지 못햇으면 -1
for i in range(n) :
if 0 in box[i] :
day = -1
return day
print(dfs())
'코딩 테스트 > 백준' 카테고리의 다른 글
[Python] 백준 1475 방 번호 (0) | 2024.12.23 |
---|---|
[Python] 백준 10808 알파벳 개수 (0) | 2024.12.23 |
[Python] 백준 1463 1로 만들기 (1) | 2024.12.22 |
[Python] 백준 25304 영수증 (0) | 2024.12.22 |
[Python] 백준 2480 주사위 세개 (2) | 2024.12.19 |