coding_test/항해99 코테스터디 TIL
99클럽 코테 스터디 _ 10일차 TIL (BFS)
aeightchill
2025. 1. 24. 15:53
728x90
< 문제 >
< 풀이 >
코드 변수 정의
- function : melt_iceberg
- new_graph : 녹은 후의 빙산 상태 그래프 생성을 위한 리스트
- directions : 상하좌우 탐색을 하기 위한 배열
- sea_cnt : 빙산에 인접한 바다의 개수
- function : count_iceberg
- iceberg_cnt : 빙산 덩어리 개수
- function : main
- graph : 현재 빙산 상태
- day : 경과 일수
- max_day : 무한 루프 방지용 최대 일수
BFS
시간복잡도 : O(N x M)
from collections import deque
# 현재 그래프 빙산 상태에서 녹은 후의 그래프를 반환하는 함수
def melt_iceberg(graph, N, M):
# 새로운 빙산 상태를 저장할 리스트 초기화
new_graph = [[0] * M for _ in range(N)]
# 상하좌우 방향 탐색
directions = [(-1,0), (0,1), (1,0), (0,-1)]
# 그래프를 순회하며 빙산의 높이를 감소
for i in range(N):
for j in range(M):
if graph[i][j] != 0: # 빙산이 있는 위치
sea_cnt = 0 # 인접한 바다 칸의 개수
for dx, dy in directions:
nx, ny = i + dx, j + dy
if 0 <= nx < N and 0 <= ny < M and graph[nx][ny] == 0:
sea_cnt += 1
# 인접한 바다 칸의 개수만큼 높이를 감소시키지만 0 미만의 값은 가지지 않음.
new_graph[i][j] = max(0, graph[i][j] - sea_cnt)
return new_graph
# 현재 빙산 상태에서 빙산 덩어리 개수를 계산하는 함수
def count_iceberg(graph, N, M):
# 빙산 덩어리 개수 초기화
iceberg_cnt = 0
visited = [[False] * (M) for _ in range(N)]
directions = [(-1,0), (0,1), (1,0), (0,-1)]
# BFS로 탐색
def bfs(graph, i, j):
q = deque([(i, j)])
while q:
x, y = q.popleft()
visited[x][y] = True
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < N and 0 <= ny < M and graph[nx][ny] != 0 and not visited[nx][ny]:
q.append((nx, ny))
visited[nx][ny] = True
for i in range(N):
for j in range(M):
# 빙산이 있고 방문하지 않았다면
if graph[i][j] != 0 and not visited[i][j]:
bfs(graph, i, j) # 같은 덩어리 내 빙산 방문 처리
iceberg_cnt += 1
return iceberg_cnt
# 빙산이 두 덩어리 이상으로 분리되는 데 걸리는 시간 계산하는 메인 함수
def main():
N, M = map(int, input().split())
graph = [list(map(int, input().split())) for _ in range(N)]
day = 0 # 경과 일수 초기화
max_day = 1000 # 최대 일수 설정
while max_day > 0:
graph = melt_iceberg(graph, N, M) # 빙산 녹이기
iceberg = count_iceberg(graph, N, M) # 현재 빙산 덩어리 수 계산
day += 1 # 하루 경과
max_day -= 1 # 최대 일수 감소
# 빙산이 2개 이상으로 분리되었다면 경과 일수 반환
if iceberg >= 2:
return day
# 두 덩어리 이상으로 분리되지 않는 경우
return 0
if __name__ == "__main__":
print(main())
풀이 방식
- melt_iceberg 함수 : 빙산 높이를 감소시키는 함수
- graph[i][j] 에 빙산이 존재할 경우(graph[i][j] != 0)
- 인접한 칸 중 바다(graph[nx][ny] == 0)의 개수를 현재 빙산의 높이에서 뺀다.
- 계산한 결과는 new_graph에 저장하여 녹은 후의 빙산 상태를 기록
- 빙산의 높이가 음수가 되지 않도록 → max(0, graph[i][j] - sea_cnt)
- 갱신한 빙산 상태를 반환
- graph[i][j] 에 빙산이 존재할 경우(graph[i][j] != 0)
- count_iceberg 함수 : 현재 빙산 덩어리 개수를 계산하는 함수
- 그래프를 순회하며 아직 방문하지 않은 빙산 칸을 찾으며 BFS를 수행
- BFS로 한 덩어리에 연결된 모든 빙산 칸을 방문 처리한 뒤, 덩어리 개수(iceberg_cnt) + 1
- 빙산 덩어리 개수를 반환
- main 함수
- 초기 빙산 상태를 입력받아 graph에 저장한다.
- 시뮬레이션 반복
- melt_iceberg를 호출해 빙산 상태를 갱신
- count_iceberg를 호출해 현재 빙산 덩어리 개수를 계산
- 덩어리가 2개 이상일 경우 현재까지 경과한 시간을 반환
- 모든 빙산이 녹을 때까지 두 덩어리 이상으로 분리되지 않으면 0 반환
- 최대 반복 횟수(max_day)를 설정하여 무한 루프를 방지한다.
< 회고 >
🤔 💭_
문제에 나온 로직을 그대로 구현하면 쉽게 풀리는 문제인 것 같다. 다만, 위의 코드는 전체 칸을 확인하므로 빙산이 있는 칸뿐만 아니라 빙산이 없는 칸까지 불필요한 탐색이 포함된다. 좀 더 효율적으로 풀 수 있는 방법에 대한 고민을 했다.
빙산이 있는 칸만 탐색하는 코드
from collections import deque
def melt_iceberg(graph, N, M, ice_locations):
melted_graph = [[0] * M for _ in range(N)] # 녹은 후 그래프
new_locations = [] # 녹은 후에도 빙산이 있는 위치 리스트
directions = [(-1,0), (0,1), (1,0), (0,-1)]
for x, y in ice_locations:
sea_cnt = 0
for dx, dy in directions:
nx, ny = x + dx, y + dy
if 0 <= nx < N and 0 <= ny < M and graph[nx][ny] == 0:
sea_cnt += 1
melted_graph[x][y] = max(0, graph[x][y] - sea_cnt)
# 녹은 후에도 빙산이 있는 경우 다시 탐색하기 위해 new_locations에 좌표 추가
if melted_graph[x][y] > 0:
new_locations.append((x, y))
return new_locations, melted_graph
def count_iceberg_groups(graph, N, M, ice_locations):
visited = [[False] * M for _ in range(N)]
directions = [(-1,0), (0,1), (1,0), (0,-1)]
groups = 0
def bfs(start_x, start_y):
q = deque([(start_x, start_y)])
visited[start_x][start_y] = True
while q:
x, y = q.popleft()
for dx, dy in directions:
nx, ny = x + dx, y + dy
if (0 <= nx < N and 0 <= ny < M and
graph[nx][ny] > 0 and not visited[nx][ny]):
q.append((nx, ny))
visited[nx][ny] = True
for x, y in ice_locations:
if not visited[x][y]:
bfs(x, y)
groups += 1
return groups
def main():
N, M = map(int, input().split())
graph = [list(map(int, input().split())) for _ in range(N)]
# 빙산이 있는 위치만 담은 리스트
ice_locations = [(i, j) for i in range(N) for j in range(M) if graph[i][j] > 0]
for days in range(1, 1001): # 최대 경과 일수 : 1000
ice_locations, graph = melt_iceberg(graph, N, M, ice_locations)
iceberg_groups = count_iceberg_groups(graph, N, M, ice_locations)
# 빙산 덩어리가 2 이상 생기는 경우
if iceberg_groups >= 2:
return days
return 0
if __name__ == "__main__":
print(main())
728x90