Algorithm/acmicpc.net

브루트포스 : 이제 좀 깊이가 생기는

winney916 2022. 1. 3. 14:56
728x90

#14500번 테트로미노 https://www.acmicpc.net/problem/14500

 

14500번: 테트로미노

폴리오미노란 크기가 1×1인 정사각형을 여러 개 이어서 붙인 도형이며, 다음과 같은 조건을 만족해야 한다. 정사각형은 서로 겹치면 안 된다. 도형은 모두 연결되어 있어야 한다. 정사각형의 변

www.acmicpc.net

 

일단 몇 가지 개념을 짚고 넘어가자. 

먼저 지금 계속 하고있는

브루트포스 (brute force) : 완전탐색 알고리즘. 가능한 모든 경우의 수를 탐색하면서 요구조건에 맞는 결과를 가져옴

 

그리고 이와 비슷한 개념인

백트래킹(Backtracing) : 되추적 이라는 의미. 이전 단계로 거슬러 올라가 다른 가능성을 찾는 방법. 모든걸 찾아보는 점에서는 브루트포스와 비슷하나 그 중에서도 가능성이 있는 것만 찾아보는 가지치기 기법을 사용. (조건을 맞출 수 없는 경우 그 경우를 제외시키는 방법)

 

백트래킹은 보통 경로탐색 알고리즘을 통해서 구현한다고 한다.

 

 

경로탐색 알고리즘이다. 여기엔 두가지 방법이 있다.

 

- 너비 우선 탐색 (Breadth-First Search : BFS)

루트노드에서부터 가까운 노드들을 순서대로 탐색하는 방법이다. 때문에 그래프의 층별로 탐색하는 형태를 띈다.

-> 한번 거친 노드 순서를 저장한 후 다시 꺼내는 선입선출 방식으로 진행 ->  큐 자료구조 사용

graph = {
    '5': ['3', '7'],
    '3': ['2', '4'],
    '7': ['8'],
    '2': [],
    '4': ['8'],
    '8': []
}

visited = []  # 방문 노드 기록
queue = []     # 큐 초기화


def bfs(visited, graph, node):  # function for BFS
    visited.append(node)
    queue.append(node)

    while queue:          # Creating loop to visit each node
        m = queue.pop(0)
        print(m, end=" ")

        for neighbour in graph[m]:
            if neighbour not in visited:
                visited.append(neighbour)
                queue.append(neighbour)


# Driver Code
print("Following is the Breadth-First Search")
bfs(visited, graph, '5')    # function calling

위 코드의 실행 결과는 5 3 7 2 4 8임을 알 수 있다. 이 알고리즘이 적용되는 문제 중 하나는 

https://programmers.co.kr/learn/courses/30/lessons/12978#

 

코딩테스트 연습 - 배달

5 [[1,2,1],[2,3,3],[5,2,2],[1,4,2],[5,3,1],[5,4,2]] 3 4 6 [[1,2,1],[1,3,2],[2,3,2],[3,4,3],[3,5,2],[3,5,3],[5,6,1]] 4 4

programmers.co.kr

프로그래머스의 배달 문제이다.

위 문제의 답은 다음과 같다.

import sys
from collections import deque


def bfs(g, graph, K, N):
    q = deque([g])  # 큐 생성
    table = [sys.maxsize]*(N+1)  # 테이블 생성. 정수 최댓값으로 초기화 <- 0도 유효한 값이기 때문에
    table[1] = 0    # 1에서 1로 가는 길이는 0. 초기화

    while q:
        temp = q.popleft()  # 큐 첫번째 값 초기 -> [1, 0]
        start_point = temp[0] # 1
        cost = temp[1]  # 0

        for i in graph[start_point]:    # 1에서 도달할 수 있는 점들로 loop
            end_point, new_cost = i[0], i[1]    # 도착점, 거리

            if cost + new_cost <= K and cost + new_cost < table[end_point]:
                # 시작 점 까지의 거리( = 현재는 0)와 도착점 까지 거리의 합이
                # K보다 작고, 이 전에 계산한 값 혹은 초기 최댓값 보다 작을 경우에만
                table[end_point] = cost+new_cost
                # 갱신
                q.append([end_point, cost+new_cost])
                # 도착점과 그 점까지의 비용을 새로 전달
    return table


def solution(N, road, K):
    answer = 0
    # make graph
    graph = [[] for _ in range(N+1)]
    for i, j, cost in road:
        graph[i].append([j, cost])
        graph[j].append([i, cost])

    # 정답리스트 받기
    lst = bfs([1, 0], graph, K, N)
    # 맥스 사이즈가 아닌 경우 -> 경로를 찾은 경우
    for i in lst:
        # 카운트
        if i != sys.maxsize:
            answer += 1

    return answer

 

 

- 깊이 우선 탐색 (Depth-First Search : DFS)

경로 하나의 모든 층을 탐색한 후 그 다음 다른 경로의 모든 층을 탐색한다.

-> 재귀 호출이나 스택을 명시하는 방법을 사용

# Using a Python dictionary to act as an adjacency list
graph = {
    '5': ['3', '7'],
    '3': ['2', '4'],
    '7': ['8'],
    '2': [],
    '4': ['8'],
    '8': []
}

visited = set()  # Set to keep track of visited nodes of graph.


def dfs(visited, graph, node):  # function for dfs
    if node not in visited:
        print(node)
        visited.add(node)
        for neighbour in graph[node]:
            dfs(visited, graph, neighbour)


# Driver Code
print("Following is the Depth-First Search")
dfs(visited, graph, '5')

위 코드의 실행 결과는 5 3 2 4 8 7임을 알 수 있다. 이번 문제에서 사용하게 될 알고리즘이다.

import sys


def dfs(x, y, value, length):
    global answer

    # 재귀함수 종료조건
    if length >= 4:
        answer = max(answer, value)
        # 재귀함수 종료를 위한 리턴
        return None

    for d in ((-1, 0), (1, 0), (0, -1), (0, 1)):
        nx = x + d[0]
        ny = y + d[1]
        if 0 <= nx < N and 0 <= ny < M and not visit[nx][ny]:
            # 방문 체크
            visit[nx][ny] = True
            # 다음지점 value 더하고 length 더해서 호출
            dfs(nx, ny, value+MAP[nx][ny], length + 1)
            # 방문 체크 해제
            visit[nx][ny] = False


def exception_check(x, y):
    global answer
    # ㅏ ㅜ ㅓ ㅗ 순으로 체크 0,0
    if 0 <= x+2 < N and 0 <= y+1 < M:  # ㅏ
        value = MAP[x][y]+MAP[x+1][y]+MAP[x+2][y]+MAP[x+1][y+1]
        answer = max(answer, value)
    if 0 <= x+1 < N and 0 <= y+2 < M:  # ㅜ
        value = MAP[x][y]+MAP[x][y+1]+MAP[x+1][y+1]+MAP[x][y+2]
        answer = max(answer, value)
    if 0 <= x-1 and x+1 < N and 0 <= y+1 < M:  # ㅓ
        value = MAP[x][y]+MAP[x-1][y+1]+MAP[x][y+1]+MAP[x+1][y+1]
        answer = max(answer, value)
    if 0 <= x-1 and 0 <= y+2 < M:  # ㅗ
        value = MAP[x][y]+MAP[x][y+1]+MAP[x][y+2]+MAP[x-1][y+1]
        answer = max(answer, value)


N, M = map(int, input().split())
MAP = [list(map(int, sys.stdin.readline().split())) for _ in range(N)]

# DFS 방문 확인
visit = [[False]*M for _ in range(N)]
answer = 0
for i in range(N):
    for j in range(M):

        # 시작지점 방문체크 해제 필요
        visit[i][j] = True
        dfs(i, j, MAP[i][j], 1)
        visit[i][j] = False

        exception_check(i, j)
print(answer)

깊이가 4일 경우로 제한하여 알고리즘을 구현하면 된다. 하지만 ㅗ 모양의 경우에는 깊이가 3일 경우에 해당한다. 그래서 추가적인 조건을 구현하거나 위와같이 별도로 체크하면 된다.

 

알고리즘의 개념이 깊어지면서 점점 코드가 길어지는걸 느낀다.

 

한참 멀었구나 나는..

 

'Algorithm > acmicpc.net' 카테고리의 다른 글

백트래킹 : 재귀를 활용한  (0) 2022.01.06
브루트포스 : 때론 조건을  (0) 2022.01.04
브루트 포스 : 리모컨  (0) 2022.01.02
브루트 포스 시작  (0) 2022.01.02
dp 기초 마지막?  (0) 2022.01.02