부트캠프

[11일차] TIL (DFS)

반응형

DFS

 

1. 전화번호 문자 조합

https://leetcode.com/problems/letter-combinations-of-a-phone-number/

 

파이썬 답게 푼 풀이입니다. 짧게 쓸 수 있다는 장점이 있습니다.

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        phone_map = {
            '2': 'abc',
            '3': 'def',
            '4': 'ghi',
            '5': 'jkl',
            '6': 'mno',
            '7': 'pqrs',
            '8': 'tuv',
            '9': 'wxyz'
        }

        if digits == "":
            return []

        numbers = list(phone_map[digits[0]])
        # ['a', 'b', 'c']

        for digit in digits[1:]:
            # 다음과 같이 이중 for문을 한줄로 작성 가능하다.
            numbers = [old + new for old in numbers for new in list(phone_map[digit])]

        return numbers

다음은 재귀함수를 이용한 풀이입니다.

 

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        def dfs(index, path):
            # 끝까지 탐색하면 백트래킹
            if len(path) == len(digits):
                result.append(path)
                return

            # 입력값 자릿수 단위반복
            for i in range(index, len(digits)):
                # 숫자에 해당하는 모든 문자열 반복
                for j in dic[digits[i]]:
                    dfs(i + 1, path + j)

    if not digits:
        return []

    dic = {
        '2': 'abc',
        '3': 'def',
        '4': 'ghi',
        '5': 'jkl',
        '6': 'mno',
        '7': 'pqrs',
        '8': 'tuv',
        '9': 'wxyz'
    }

    result = []
    dfs(0, "")

    return result

 

 

2. 순열

https://leetcode.com/problems/permutations/

 

처음 문제를 보자마자 permutations로 풀면 짧게 풀 수 있겠다는 생각을 했습니다.

class Solution:
    from itertools import permutations
    def permute(self, nums: List[int]) -> List[List[int]]:
        return permutations(nums, len(nums))

하지만 DFS챕터이므로 DFS로 푸는 방법을 적어보겠습니다.(재귀)

 

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        results = []
        prev_elements = []

        def dfs(elements):
            # 리프 노드일때 결과 추가
            if len(elements) == 0:
                results.append(prev_elements[:])

            # 순열 생성 재귀 호출
            for e in elements:
                next_elements = elements[:] # 참조가 아닌 값을 복사한 리스트
                next_elements.remove(e) # 리스트에서 하나씩 빼주면서 순서를 바꾼 리스트 만들기

                prev_elements.append(e)
                dfs(next_elements)
                prev_elements.pop() # 다음 차례를 위해 값을 빼준다

        dfs(nums)
        return results

다음은 백트래킹시 유용하게 쓰일 템플릿을 첨부하겠습니다.

def dfs(node, state):
    if state is a solution:
        report(state) # e.g. add state to final result list
        return

    for child in children:
        if child is a part of a potential solution:
            state.add(child) # make move
            dfs(child, state)
            state.remove(child) # backtrack

 

3. 조합

https://leetcode.com/problems/combinations/

위 순열 문제와 마찬가지로 itertools의 combinations를 쓰면 2줄에 풀 수 있었습니다.

 

class Solution:
    from itertools import combinations
    
    def combine(self, n: int, k: int) -> List[List[int]]:
        lst = [i for i in range(1, n + 1)]
        return combinations(lst, k)

 

다음은 DFS를 이용해서 푼 문제입니다.(재귀)

 

 

4. 단지번호 붙이기

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

 

처음 스택을 이용한 방법이 떠올라 풀어보고자 했는데 실패하였습니다.

다음은 실패한 코드입니다.

n = int(input())

graph = []
for _ in range(n):
    graph.append(list(map(int, input())))

# 북, 동, 남, 서
dx = [-1, 0, 1, 0]
dy = [0, 1, 0, -1]

print(graph)

danji_list = []
stack = []
danji_cnt = 0
for row in range(n):
    for col in range(n):
        if graph[row][col] != '1':
            continue

        danji_cnt += 1
        danji_result = 0
        stack = [(row, col)]
        while stack:
            x, y = stack.pop()
            graph[x][y] = '0'
            for i in range(4):
                nx = x + dx[i]
                ny = y + dy[i]
                if nx < 0 or ny < 0 or nx >= n or ny >= n or graph[nx][ny] == '0':
                    continue
                stack.append((nx, ny))
                danji_result += 1
        danji_list.append(danji_result)

print(danji_cnt) # 단지 수
for cnt in danji_list:
    print(cnt)

 

다음은 재귀함수를 이용한 풀이입니다.

 

n = int(input())
graph = []
num = []

for i in range(n):
    graph.append(list(map(int, input())))

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

print(graph)

def DFS(x, y):
    if x < 0 or x >= n or y < 0 or y >= n:
        return False

    if graph[x][y] == 1:
        global count
        count += 1
        graph[x][y] = 0
        for i in range(4):
            nx = x + dx[i]
            ny = y + dy[i]
            DFS(nx, ny)
        return True
    return False


count = 0
result = 0

for i in range(n):
    for j in range(n):
        if DFS(i, j) == True:
            num.append(count)
            result += 1
            count = 0

num.sort()
print(result)
for i in range(len(num)):
    print(num[i])

 

5. 바이러스

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

 

BFS로 풀어본 풀이입니다.

n = int(input())
m = int(input())

from collections import deque

graph =[[] * i for i in range(n + 1)]
for _ in range(m):
    a, b = map(int, input().split())
    graph[a].append(b)
    graph[b].append(a)

count = 0
visited = [False] * (n + 1)

q = deque([1])

while q:
    node = q.popleft()
    visited[node] = True
    for i in graph[node]:
        if visited[i] == False:
            q.append(i)

# print(visited)

print(sum(visited) - 1)

 

추가로 DFS로도 풀어보았습니다.

위의 BFS도 마찬가지지만 True, False 를 1과 0으로 계산하여 sum 메소드를 사용할 수 있다는 것을 알 수 있습니다.

n = int(input())
m = int(input())

from collections import deque

graph =[[] * i for i in range(n + 1)]
for _ in range(m):
    a, b = map(int, input().split())
    graph[a].append(b)
    graph[b].append(a)

visited = [False] * (n + 1)

def dfs(graph, node, visited):
    visited[node] = True
    for i in graph[node]:
        if not visited[i]:
            dfs(graph, i, visited)

    return

dfs(graph, 1, visited)
print(sum(visited) - 1)
반응형

'부트캠프' 카테고리의 다른 글

[13일차] TIL - Back Tracking  (0) 2022.03.21
[알고리즘 1주차] WIL  (0) 2022.03.20
[10일차] TIL - Week1 Test  (0) 2022.03.17
[9일차] TIL (Hash Table)  (0) 2022.03.16
[5일차] TIL (문자열 다루기)  (0) 2022.03.13