파이썬 알고리즘/BOJ

[Algorithm] 백준 #4963 - 섬의 개수

walbe0528 2022. 5. 20. 13:26
728x90
반응형

https://www.acmicpc.net/problem/4963

 

4963번: 섬의 개수

입력은 여러 개의 테스트 케이스로 이루어져 있다. 각 테스트 케이스의 첫째 줄에는 지도의 너비 w와 높이 h가 주어진다. w와 h는 50보다 작거나 같은 양의 정수이다. 둘째 줄부터 h개 줄에는 지도

www.acmicpc.net

 

📌 다시 봐야 할 부분

 

1. dx, dy를 모두 돌면서 확인해줄 때, 범위 벗어난 처리를 먼저!

if nx < 0 or nx >= w or ny < 0 or ny >= h:  # 범위를 벗어난 경우
    continue

이 부분을 먼저 적어주어야한다. 범위를 벗어난 경우의 처리를 아래로 빼주고 육지가 아닌 경우나 땅인 경우를 먼저 처리하려고 하면, out of index오류가 발생한다. 

 

 

주로 다른 bfs문제에서는 상하좌우만을 보면 되는데, 이 문제는 대각선으로 연결되었을때까지 확인해줘야 한다.

상하좌우 + 대각선 연결시 이동가능 -> 아래와 같이 총 8개로 잡아줘야한다

dx = [0, 0, 1, 1, 1, -1, -1, -1]
dy = [-1, 1, -1, 0, 1, -1, 0, 1]

 

 

일반적인 상하좌우 문제에서는 dx, dy를 아래처럼 잡아준다.

dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]

 

2. 그리고 나의 삽질

 

맞게 푼 것 같은데 자꾸 index out of range가 발생해서 살펴보니, 행과 열의 확인 범위를 반대로 적어줬다.....ㅇㅏ..?

for i in range(h):
    for j in range(w):
        if graph[i][j] == 1:  #1(육지)이면
            result += 1
            bfs(i, j)

while문 안에서 i의 범위는 h이고, j의 범위는 w이므로, 

 

 

if nx < 0 or nx >= h or ny < 0 or ny >= w:  # 범위를 벗어난 경우

 

이게 맞는거다. h랑 w를 계속 반대로 적어줬다....

 

 

3. 방문처리

 

visited 배열을 만들어서 방문처리를 해주려고 했으나,

bfs함수로 확인을 하면서, 육지 방문시 방문된 것의 표시로 육지(1) -> 바다(0)로 바꿔주는 방법을 사용했다. 

 

 

📚 코드

import sys
from collections import deque

dx = [0, 0, 1, 1, 1, -1, -1, -1]  # 가로, 세로, 대각선 연결시 이동가능 -> 총 9개로 잡아줘야한다
dy = [-1, 1, -1, 0, 1, -1, 0, 1]


def bfs(x, y):  # 1이 땅, 0이 바다
    queue = deque()
    queue.append([x, y])

    while queue:
        x, y = queue.popleft()
        for i in range(8):
            nx = x + dx[i]
            ny = y + dy[i]
            if nx < 0 or nx >= h or ny < 0 or ny >= w:  # 범위를 벗어난 경우
                continue
            if graph[nx][ny] == 0:  # 바다인경우 -> 계속하기
                continue
            if graph[nx][ny] == 1:  # 땅인경우
                graph[nx][ny] = 0  # visited를 사용하지 않고 방문한 땅은 0으로 바꿔준다!
                queue.append([nx, ny])  # 큐에 넣어주기

while True:
    w, h = map(int, sys.stdin.readline().rstrip().split())  # 너비, 높이
    if (w == 0) and (h == 0):
        break
    graph=[]
    for _ in range(h):
        graph.append(list(map(int, sys.stdin.readline().rstrip().split())))  # 지도 입력
    # visited = [[False] * w for _ in range(h)]
    result = 0
    for i in range(h):
        for j in range(w):
            if graph[i][j] == 1:  #1(육지)이면
                result += 1
                bfs(i, j)
    print(result)

 

 

 + dfs, bfs 두 방식으로 모두 푸는 것이 가능하다고 한다.

나는 bfs방식으로 풀어서 런타임에러(RecursionError) 가 나지 않았지만, dfs로 푼 경우 이를 위해

sys.setrecursionlimit(100000) 코드를 추가해줘야 한다. 

이유 : 파이썬의 재귀 깊이는 기본이 1000으로 설정되어 있는데, 이 문제의 제한에서는 깊이가 최대 2500까지 들어갈 수 있어 문제가 된다고 한다!! (w <= 50, h <=50 범위 확인!)

728x90