코딩테스트 연습

코테 연습) 그래프탐색 알고리즘 : DFS/BFS(DFS 문제 : 음료수 얼려 먹기).python

37_KIM 2022. 7. 22. 18:20

 

 

q1) 음료수 얼려먹기

- N * M 크기의 얼음 틀이 있다 구멍이 뚫려있는 부분은 0 칸막이가 존재하는 부분은 1로 표시 

구멍이 뚫려 있는 부분까리 상, 하, 좌, 우로 붙어 있는 경우 서로 연결되어 있는 것으로 간주

이 때 얼음 틀의 모양이 주어졌을 때 생성되는 총 아이스크림의 개수는?

 

4 * 5 얼음 틀 예시에서는 아이스크림이 총 3개 생성된다

왼쪽의 숫자와 같이 2차원 리스트로 행이4 열이5인 4 * 5 행렬 생성

- 문제의 유형은 연결요소 찾기 즉 커넥티드 컴포넌트라고 볼 수 있다

- 이 문제는 DFS혹은 BFS로 해결 할 수 있다

- 얼음을 얼릴 수 있는 공간이 상, 하, 좌, 우로 연결되어 있다고 표현할 수 있으므로 그래프 형태로 모델링 할 수 있다

- 상, 하, 좌, 우로 연결된 포인트는 서로 인접한 노드 현태로 표현할 수 있다

맨 왼쪽 상단의 0을 기준으로 인접한 노드를 구해놓고 1을 제외하고 방문하지 않은 노드를 찾는 방식으로 문제를 해결하면 될 것 같다 

- 특정 지점의 주변 상, 하, 좌, 우를 살펴본 뒤 주변 지점에서 값이 '0'이면서 아직 방문하지 않은 지점이 있다면 해당 지점 방문

- 방문한 지점에서 다시 상, 하, 좌, 우를 살펴보면서 방문을 진행하는 과정을 반복하면 연결된 모든 지점 방문

- 모든 노드에 대하여 위의 과정을 반복하여 방문하지 않은 지점의 수를 카운트

 

->입력창

# DFS로 특정 노드를 방문하고 연결된 모든 노드들도 방문
def dfs(x, y):
    # 주어진 범위를 벗어나는 경우에는 즉시 종료
    if x <= -1 or x >= n or y <= -1 or y >= m:
        return False
    # 현재 노드를 아직 방문하지 않았다면
    if graph[x][y] == 0:
        # 해당 노드 방문 처리
        graph[x][y] = 1
        # 상, 하, 좌, 우의 위치들도 모두 재귀적으로 호출
        dfs(x - 1, y)
        dfs(x, y - 1)
        dfs(x + 1, y)
        dfs(x, y + 1)
        return  True
    return False
# N, M을 공백을 기준으로 구분하여 입력받기
n, m = map(int, input().split())

# 2차원 리스트의 맵 정보 입력 받기
graph = []
for i in range(n):
    graph.append(list(map(int, input())))

# 모든 노드(위치)에 대하여 음료수 채우기
result = 0
for i in range(n):
    for j in range(m):
        # 현재 위치에서 DFS 수행
        if dfs(i, j) == True:
            result += 1

print(result) # 정답 출력

 

->출력창 4 * 5행렬을 만들기 위해 n m값 4 5를 공백을 두고 엔터, 앞 행렬의 행과 열만큼의 2차원 리스트를 만들기 위해 

00110 앤터 00011 엔터 11111 엔터 00000엔터 출력 3

C:\Users\admin\PycharmProjects\ML_EX\venv\Scripts\python.exe C:/Users/admin/PycharmProjects/ML_EX/study05.py
4 5
00110
00011
11111
00000
3

종료 코드 0(으)로 완료된 프로세스

 

풀이)

# DFS로 특정 노드를 방문하고 연결된 모든 노드들도 방문
def dfs(x, y):
    # 주어진 범위를 벗어나는 경우에는 즉시 종료
    if x <= -1 or x >= n or y <= -1 or y >= m:
        return False

- x, y 함수를 만든다 만약 x가 -보다 작거나 같거나 x가 n보다 크거나 같거나 y가 -1보다 작거나 같거나 y가 m보다 크거나 같으면 종료 그렇지 않은경우 아래 조건문을 수행


    # 현재 노드를 아직 방문하지 않았다면
    if graph[x][y] == 0:

- 현재 노드를 아직 방문하지 않았다면 아래 조건을 수행


        # 해당 노드 방문 처리
        graph[x][y] = 1

- 제시한 바가 없으니 가장 작은 수 0,0부터 방문해서 0,0은 이제 방문처리를 한다


        # 상, 하, 좌, 우의 위치들도 모두 재귀적으로 호출
        dfs(x - 1, y) # 좌(0,0 넣었을 때)
        dfs(x, y - 1) # 하(0,0 넣었을 때)
        dfs(x + 1, y) # 상(0,0 넣었을 때)
        dfs(x, y + 1) # 우(0,0 넣었을 때)
        return  True

4행 5열을 기준으로 상 하 좌 우를 살펴보자 0,0기준 우(0,1), 하(1,0)는 0이므로 방문하지 않은 인접노드가 된다 

0(0,0) 0(0,1) 1(0,2) 1(0,3) 0(0,4)
0(1,0) 0(1,1) 0(1,2) 1(1,3) 1(1,4)
1(2,0) 1(2,1) 1(2,2) 1(2,3) 1(2,4)
0(3,0) 0(3,1) 0(3,2) 0(3,3) 0(3,4)

- 첫번째 x,y에 0,0을 대입 했을 때 좌(-1,0), 하(0,-1)는 -1이 있으므로 종료, 우(1,0), 상(0,1)은 방문처리

1(0,0) 1(0,1) 1(0,2) 1(0,3) 0(0,4)
1(1,0) 0(1,1) 0(1,2) 1(1,3) 1(1,4)
1(2,0) 1(2,1) 1(2,2) 1(2,3) 1(2,4)
0(3,0) 0(3,1) 0(3,2) 0(3,3) 0(3,4)

 

 

 

- 다시 반복문 을 1,0과 0,1중에 작은 수 부터 차례로 방문하지 않은 노드부터 방문처리 

- 잘 모르지만 작은 수로 따졌을 때 우(0,1)과 하(1,0)에서 x가 우선이니까 0,1이 작은거 같아서 0,1 노드부터 상, 하, 좌, 우 방문 

 

- 위 재귀적 호출에서 0,1 대입해보면

좌(-1,1)는 -1이 있으니까 종료 나며지 하(0,0), 우(0,2)는 이미 1이므로 방문노드이고 나머지 방문하지 않은 노드 상(1,1)에 방문해서 방문처리 이제는 0,0근방에 방문하지 않은 노드기 없으니

 

1(0,0) 1(0,1) 1(0,2) 1(0,3) 0(0,4)
1(1,0) 1(1,1) 0(1,2) 1(1,3) 1(1,4)
1(2,0) 1(2,1) 1(2,2) 1(2,3) 1(2,4)
0(3,0) 0(3,1) 0(3,2) 0(3,3) 0(3,4)

 

- 이제 1,1 대입해서 방문하지 않은 노드 방문 표에서 좌(0,1), 하(1,0), 상(2,1)은 이미 방문노드이고 우(1,2)만 방문하지 않은 노드이므로 방문처리

1(0,0) 1(0,1) 1(0,2) 1(0,3) 0(0,4)
1(1,0) 1(1,1) 1(1,2) 1(1,3) 1(1,4)
1(2,0) 1(2,1) 1(2,2) 1(2,3) 1(2,4)
0(3,0) 0(3,1) 0(3,2) 0(3,3) 0(3,4)

 

- 그다음 1,2를 대입해보면 좌(0,1), 하(1,0), 상(2,2), 우(1,3) 모두 방문노드이므로  이제 리턴값이 True가 아님 아래처럼

 

    return False

- 종료하고 (현재 위치가 처음위치(0,0)에서 처음 dfs가 수행이 된 것이기 때문에 그 위치(0,0)에 대해서 아래 for문 아래 result값을 증가시킨다)

 

# N, M을 공백을 기준으로 구분하여 입력받기
n, m = map(int, input().split())

- 공백을 기준으로 행열값 입력받기


# 2차원 리스트의 맵 정보 입력 받기
graph = []
for i in range(n):

- 행값을 0부터 n-1까지 차례로 꺼낸거를 i라는 변수로 두고 아래 조건문 반복 
    graph.append(list(map(int, input())))
- n줄에 걸쳐서 2차원 리스트 맵정보를 입력받는다 즉 입력받은 n줄을 그래프 리스트에 채워준다

-  이때 입력은 공백없이 0과 1로 구성된 문자열 형태(맨위에 조건 제시해줌 " if x <= -1 or x >= n or y <= -1 or y >= m:")로 주어지기 때문에 한줄로 입력받은 다음 각 원소를 정수형으로 변환해주고 리스트로 만든다 그럼 이제 모든 원소가 0 혹은 1인 정수 리스트가 만들어진다


# 모든 노드(위치)에 대하여 음료수 채우기
result = 0
for i in range(n):

- 이제 리스트형태로 되어 있는 행값을 0부터 n-1까지 차례로 꺼낸거를 i라는 변수로 두고 아래 조건문 반복

 

    for j in range(m):

- 리스트형태로 되어있는 열값을 0부터 m-1까지 차례로 꺼낸거를  j라는 변수로 두고 아래 조건문 반복

- n * m 위치에서 dfs를 실행 0,0에서 실행하고 1,2에서 종료되었으니 DFS는 0,0으로 시작이 되었으므로


        # 현재 위치에서 DFS 수행
        if dfs(i, j) == True:
            result += 1

- 0,0지점에서 result값이 +1이 되는거 (0,0부터 인접 노드들을 1로 만든 한 뭉텅이가 하나로 카운팅된다)

(현재 위치에 대해서 이번에 dfs를 수행해서 처음 방문처리가 되었다면 그 때 카운트(+1) 진행)

 

- 표에서 보면 다음 반복문의 시작은 0,4 그 다음 반복문의 시작은 3.0으로 

 
print(result) # 정답 출력 

- result값은 3이 된다!

 

 

 

 

 

출처 / 유튜브 [동빈나 - (이코테2021) 이것이 취업을 위한 코딩 테스트다 with 파이썬]