minjeong-oh
기록
minjeong-oh
전체 방문자
오늘
어제
  • 분류 전체보기 (207)
    • ML & Neural Net (3)
    • ◼️GNN (1)
    • ◼️시계열 (1)
    • ◼️추천시스템 (0)
    • ◼️수학 (3)
    • Data Engineer (2)
    • ◼️Spark (1)
    • ◼️Kafka (1)
    • ◼️Elestic Search (0)
    • ◼️Redis (0)
    • ◼️ClickHouse (1)
    • Programming Language (4)
    • ◼️SQL (0)
    • ◼️React (0)
    • ◼️Git (1)
    • ◼️Python (1)
    • ◼️C++ (33)
    • ◼️Fortran 99 (2)
    • ◼️OpenGL (7)
    • ◼️MFC (35)
    • ◼️Flutter (46)
    • ◼️Kotlin (1)
    • ◼️Android (1)
    • ◼️Java (1)
    • ◼️C (4)
    • Development (0)
    • ◼️디자인패턴 (0)
    • ◼️네트워크 (2)
    • ◼️인증 (1)
    • Computer Science (4)
    • 알고리즘 문제풀이 (12)
    • SQL 고득점 Kit 문제풀이 (8)
    • 취업준비 (0)
    • Notion 정리 글 (1)
    • Article Scrap (12)
      • Careerly - Q&A (7)
      • Careerly - Post (2)
    • 기타 등등 (16)

블로그 메뉴

  • 글쓰기
  • 홈
  • 태그
  • 방명록
  • 편집

공지사항

인기 글

태그

  • mfc
  • 구조체매개변수
  • API문서정리
  • 이것이C++이다책참고
  • OpenGL회전
  • NextJSSwagger
  • 그램업그레이드
  • 그램풀스팩업그레이드
  • 이차원구조체배열포인터
  • SpringBootSwagger
  • 19년식그램램장착
  • 리눅스파티션
  • 19년식그램SSD장착
  • GSLB
  • AI배워야하나
  • 구조체배열포인터
  • hello테마
  • 티스토리폰트배경색없애기
  • 인공지능개발자
  • kafka구축

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
minjeong-oh

기록

알고리즘 문제풀이

깊이 우선 탐색(DFS) 문제 및 풀이 (Python)

2023. 12. 4. 20:00

코테 준비를 위한 알고리즘 공부 순서 

 위 링크에서 깊이 우선 탐색(DFS) 알고리즘 문제를 풀고 풀이를 기록하는 게시글입니다. 

 

 

2. 깊이 우선 탐색(DFS)

  • 루트 노드(혹은 다른 임의의 노드)에서 시작해서 다른 가지(branch)로 넘어가기 전에 현재 가지(branch)에 연결되어있는 애들을 완벽하게 탐색하는 방법
  • 미로를 탐색할 때 한 방향으로 갈 수 있을 때까지 계속 가다가 더 이상 갈 수 없게 되면 다시 가장 가까운 갈림길로 돌아와서 이곳으로부터 다른 방향으로 다시 탐색을 진행하는 방법과 유사하다
  • 넓게(wide) 탐색하기 전에 깊게(deep) 탐색하는 것이다.
  • 깊이 우선 탐색(DFS)이 너비 우선 탐색(BFS)보다 좀 더 간단하다.
  • 단순 검색 속도 자체는 너비 우선 탐색(BFS)에 비해서 느리다.
  • 그래프 탐색의 경우 어떤 노드를 방문했었는지 여부를 반드시 검사 해야 한다는 것이다.
    이를 검사하지 않을 경우 무한루프에 빠질 위험이 있다

참고링크

 

DFS 기본 예제

더보기
  • 방문 여부를 저장하기위해 리스트를 다음과 같이 만든다. visited = [False] * N
  • 데이터는 graph 변수에 리스트로 저장한다.
  • 현재 노드와 연결된 자식노드를 재귀적으로 방문한다.

1. 재귀

# DFS 메서드 정의
def dfs(graph, v, visited):
    #현재 노드를 방문 처리
    visited[v] = True
    print(v, end=' ')
    # 현재 노드와 연결된 자식 노드를 재귀적으로 방문
    for i in graph[v]:
        if not visited[i]:
            dfs(graph, i, visited)

# 각 노드가 연결된 정보를 리스트 자료형으로 표현(2차원 리스트)
graph = [
    [],
    [2, 3, 8],
    [1, 7],
    [1, 4, 5],
    [3, 5],
    [3, 4],
    [7],
    [2, 6, 8],
    [1, 7]
]

# 각 노드가 방문된 정보를 리스트 자료형으로 표현(1차원 리스트)
visited = [False] * 9

dfs(graph, 1, visited)

코드출처

 

 

2. for문 (stack 활용)

def dfs(graph, start):
    visited = set()  # 방문한 노드를 저장
    stack = [start]  # 스택 초기화 (시작 노드 추가)
    
    while stack:
        node = stack.pop()  # 스택에서 노드를 꺼냄
        if node not in visited:
            visited.add(node)  # 방문 처리
            # 현재 노드의 인접 노드들 중 방문하지 않은 노드를 스택에 추가
            for neighbor in graph[node]:
                if neighbor not in visited:
                    stack.append(neighbor)
    
    return visited

 

 

문제

(❌ 표시는 혼자 못푼 문제)

 

2023. 12. 06. 2일차(1)

1. 1012번 유기농 배추 (실버2)  문제링크

더보기

문제 해석

농약을 쓰지 않고 유기농 배추를 재배하려고한다. 

해충 방지를 위해 배추 흰지렁이를 구입하려고한다.

이 지렁이는 배추근처에 서식하며 해충을 잡아먹으며 배추를 보호한다.

어떤 배추에 배추흰지렁이가 한마리라도 살고 있으면 이 지렁이는 인접한(상하좌우) 다른 배추로 이동이 가능한다. 그 배추들 역시 해충으로부터 보호됨.

배추들이 모여있는 곳에는 배추흰지렁이가 한마리 필요.

인접해있는 배추들이 몇군데 퍼져있는지 조사하여 총 몇마리의 지렁이가 필요한지 구해보자.

0: 배추가 심어져있지 않은 땅

1: 배추가 심어져 있는 땅

제한: 1초 512MB

 

입력

입력의 첫 줄에는 테스트 케이스의 개수 T가 주어진다. 그 다음 줄부터 각각의 테스트 케이스에 대해 첫째 줄에는 배추를 심은 배추밭의 가로길이 M(1 ≤ M ≤ 50)과 세로길이 N(1 ≤ N ≤ 50), 그리고 배추가 심어져 있는 위치의 개수 K(1 ≤ K ≤ 2500)이 주어진다. 그 다음 K줄에는 배추의 위치 X(0 ≤ X ≤ M-1), Y(0 ≤ Y ≤ N-1)가 주어진다. 두 배추의 위치가 같은 경우는 없다.

 

출력

각 테스트 케이스에 대해 필요한 최소의 배추흰지렁이 마리 수를 출력한다.

 

코드

31MB, 56ms

import sys
sys.setrecursionlimit(10**6)


def _input():
    return sys.stdin.readline().strip()


def dfs(x, y):
    dx = [-1, 1, 0, 0]
    dy = [0, 0, -1, 1]

    # 상하좌우 인접한 땅에 배추가 심어져있는지 확인한다.
    visited[x][y] = True
    for k in range(4):
        nx = x + dx[k]
        ny = y + dy[k]

        # 유효한 범위인 경우에만 실행
        if -1 < nx < N and -1 < ny < M \
                and not visited[nx][ny] and graph[nx][ny] == 1:
            dfs(nx, ny)


case = int(_input())
for _ in range(case):
    M, N, K = map(int, _input().split())
    graph = [[0] * M for i in range(N)]
    visited = [[False] * M for i in range(N)]

    for i in range(K):
        x, y = map(int, _input().split())
        graph[y][x] = 1

    count = 0
    for i in range(N):
        for j in range(M):
            if graph[i][j] == 1 and not visited[i][j]:
                dfs(i, j)
                count += 1

    print(count)

 

이 문제의 포인트

* RecursionError  (참고)

    - Python이 정한 최대 재귀 깊이: BOJ의 채점 서버에서 이 값은 1,000으로 되어 있다. 

    - 재귀를 사용하지 않거나 sys.setrecursionlimit(10**6) 코드로 최대 깊이를 1,000,000 정도로 크게 설정해 런타임에러 없이 실행할 수 있다.

    - 최대 깊이 제한이 너무 작을 때도 RecursionError가 발생합니다.

    - 최댓값을 크게 변경했다고 하더라도, 재귀의 깊이가 채점 서버가 감당할 수 없는 정도로 깊어지면, Segmentation fault가 발생해 런타임 에러 이유로 SegFault를 받게됩니다.

* 상하좌우를 확인할 때 dx, dy로 방향을 이동한다.

2023. 12. 06. 2일차(2)

2. 11724번 연결 요소의 개수 (실버2)  문제링크

더보기

문제 해석

방향 없는 그래프가 주어졌을 때, 연결 요소의 개수를 구하는 프로그램을 작성해보자.

제한: 3초 512MB

 

입력

첫째 줄에 정점의 개수 N과 간선의 개수 M이 주어진다. (1 ≤ N ≤ 1,000, 0 ≤ M ≤ N×(N-1)/2) 둘째 줄부터 M개의 줄에 간선의 양 끝점 u와 v가 주어진다. (1 ≤ u, v ≤ N, u ≠ v) 같은 간선은 한 번만 주어진다.

 

출력

첫째 줄에 연결 요소의 개수를 출력한다.

 

코드

800ms

import sys
sys.setrecursionlimit(10**6)


def _input():
    return sys.stdin.readline().strip()



def dfs(i):
    visited[i] = True

    for k in graph[i]:
        if not visited[k]:
            dfs(k)


N, M = map(int, _input().split())
graph = [[] for _ in range(N+1)]
visited = [False] * (N+1)

for _ in range(M):
    u, v = map(int, _input().split())
    graph[u].append(v)
    graph[v].append(u)

count = 0
for i in range(1, N + 1):
    if not visited[i]:
        dfs(i)
        count += 1

print(count)

2023. 12. 06. 2일차(3)

3. 11403번 경로 찾기 (실버1)  문제링크

더보기

문제 해석

가중치 없는 방향 그래프 G가 있다. 

모든 정점 (i, j)에 대해서 i에서 j로 가는 길이가 양수인 경로가 있는지 없는지 구하는 프로그램을 작성하여라.

제한: 1초 256MB

 

입력

첫째 줄에 정점의 개수 N (1 ≤ N ≤ 100)이 주어진다. 둘째 줄부터 N개 줄에는 그래프의 인접 행렬이 주어진다. i번째 줄의 j번째 숫자가 1인 경우에는 i에서 j로 가는 간선이 존재한다는 뜻이고, 0인 경우는 없다는 뜻이다. i번째 줄의 i번째 숫자는 항상 0이다.

 

출력

총 N개의 줄에 걸쳐서 문제의 정답을 인접행렬 형식으로 출력한다. 정점 i에서 j로 가는 길이가 양수인 경로가 있으면 i번째 줄의 j번째 숫자를 1로, 없으면 0으로 출력해야 한다.

 

코드

i 에서 j로 갈수 있는 경로가 존재하는지 확인하는 것. 경로가 존재하면 i,j 행렬에 1을 출력하면된다.

i == j 같을 경우도 확인해줘야 하기 때문에 처음 시작하는 i는 방문안하고 시작한다. 나중에 방문할 수 있어야 i->j 경로가 확인되기 때문 (BFS로 푼것)

34MB, 120ms

import sys
from collections import deque


def _input():
    return sys.stdin.readline().strip()


def bfs(start, target):
    dq = deque([start])
    
    visited = [False] * (N + 1)
    while dq:
        x = dq.popleft()
        for k in graph[x]:
            if k == target:
                return 1

            if not visited[k]:
                visited[k] = True
                dq.append(k)

    return 0


N = int(_input())
graph = [[] for _ in range(N)]
result = [[0] * N for _ in range(N)]

for i in range(N):
    arr = list(map(int, _input().split()))
    for j in range(N):
        if arr[j] == 1:
            graph[i].append(j)

#i->j 결과를 dfs 한번으로 반환받는다.
for i in range(N):
    for j in range(N):
        result[i][j] = bfs(i, j)

for i in range(N):
    for j in range(N):
        print(result[i][j], end=" ")

    print()

 

이 문제의 포인트

* deque는 리스트등과 같이 iterable 기반의 데이터를 인자로 받아서 deque 생성이 가능. deque([1,2]) or deque('love') or deque([])

* 재귀를 사용하지 않은 dfs이다. 

 

추가문제

더보기

4. 2468번 안전 영역 (실버1)  문제링크

 

5. 1743번 음식물 피하기 (실버1)  문제링크

 

6. 2667번 단지번호붙이기 (실버1)  문제링크

 

7. 2583번 영역 구하기 (실버1)  문제링크

 

8. 10026번 적록색약 (골드5)  문제링크

 

9. 2668번 숫자고르기 (골드5)  문제링크

 

10. 1987번 알파벳 (골드4)  문제링크

 

11. 9466번 텀 프로젝트 (골드3)  문제링크

 

12. 1103번 게임 (골드2)  문제링크

 

13. 10265번 MT (플레4)  문제링크

 

 

 

 

 

'알고리즘 문제풀이' 카테고리의 다른 글

동적계획법(DP) 문제 및 풀이 (Python)  (0) 2023.12.04
완전탐색(브루트포스) 문제 및 풀이 (Python)  (0) 2023.12.04
너비 우선 탐색(BFS) 문제 및 풀이 (Python)  (0) 2023.12.04
그리디 알고리즘 문제 및 풀이 (Python)  (2) 2023.10.01
코테 준비를 위한 알고리즘 공부 순서  (0) 2023.09.25

    티스토리툴바