728x90
 

GitHub - Seungkyu-Han/PS_strategy: 알고리즘 분류하는 워크플레이스

알고리즘 분류하는 워크플레이스. Contribute to Seungkyu-Han/PS_strategy development by creating an account on GitHub.

github.com

이전에도 풀었던 문제이지만 알고리즘을 복습하면서 다시 풀어보았다.

백준을 위해서 푼 건 아니지만 백준에 문제가 있기는 하다.

 

2026번: 소풍

만약 K명의 친구 관계인 학생들이 존재하지 않는다면 -1을 출력한다. 그 외의 경우에는, K개의 줄에 학생들의 번호를 증가하는 순서로 한 줄에 한 개씩 출력한다. 여러 경우가 존재한다면 첫 번째

www.acmicpc.net

요즘 브루투 포스 알고리즘을 풀면서 생각하는 건데 브루트 포스 문제는 백트래킹 기법을 같이 사용해서 푸는 문제가 많은 것 같다.

재귀함수를 이용하다보니 조건을 만족하면 되돌아 오는 백트래킹을 많이 쓰는 것 같다.

덕분에 함수 다루는 실력이 좀 향상된것 같다. ㅎㅎ (지극히 내 생각이다.....)

 

함수를 2개 사용했다.

첫 번째 함수는 현재 있는 멤버에 새로운 친구가 들어갈 수 있는지 확인해주는 함수이다.

# 새로 들어온 친구가 들어갈 수 있는지 확인하는 함수이다.
# 리스트에 첫 번째 인덱스는 0이 있으니 제외하고 시작하자.
def is_friend(plus_friend, member_list):
    for q in member_list[1:]:
        if plus_friend not in friends[q]:
            return False
    return True

굳이 설명할 필요가 없을 거라고 생각된다.

 

두번째 함수는 재귀함수 + 백트래킹 함수 + 탐색 함수이다.

이 함수가 핵심이다.

# cur_cnt는 현재 만들어진 멤버의 수, cur_list는 그 멤버의 번호이다.
def find_friends(cur_cnt, cur_list):

    # 멤버를 K명 모으면 함수를 종료한다. (재귀함수의 기저사례)
    if cur_cnt == K:
        for q in cur_list[1:]:
            print(q)
        return True

    # 순서대로 들어가기 때문에 cur_list에 있는 번호가 가장 큰 멤버는 -1의 인덱스에 있다.
    # 대신 빈 리스트에 -1을 부르면 에러가 뜨기 때문에 이 함수 호출 전에 반복문으로 0을 넣어주고 시작한다.
    for q in range(cur_list[-1] + 1, N + 1):
        if is_friend(q, cur_list):
            cur_list.append(q)
            if find_friends(cur_cnt + 1, cur_list):
                return True
            cur_list.pop()
    return False

우선 함수가 종료되는 조건은 멤버의 수가 K가 되는 조건이다.

글을 작성하면서 생각해보니 굳이 cur_cnt를 쓰지 않고 len함수를 이용해서 인수를 하나 줄일 수 있었을 것 같다.

 

아래 반복문은 아직 확인하지 않은 값을 is_friend로 확인하면서 가능하다면 해당 멤버를 추가해 다시 함수를 호출하는 부분이다.

백트래킹에서 사용하듯이 만약 해당 값으로 답을 찾을 수 없다면 다 확인해본 후에 pop등을 이용해서 값을 빼주어야 한다.

import sys

K, N, F = map(int, sys.stdin.readline().split())

friends = [[] for i in range(N + 1)]

for i in range(F):
    a, b = map(int, sys.stdin.readline().split())
    friends[a].append(b)
    friends[b].append(a)


# 새로 들어온 친구가 들어갈 수 있는지 확인하는 함수이다.
# 리스트에 첫 번째 인덱스는 0이 있으니 제외하고 시작하자.
def is_friend(plus_friend, member_list):
    for q in member_list[1:]:
        if plus_friend not in friends[q]:
            return False
    return True


# cur_cnt는 현재 만들어진 멤버의 수, cur_list는 그 멤버의 번호이다.
def find_friends(cur_cnt, cur_list):

    # 멤버를 K명 모으면 함수를 종료한다. (재귀함수의 기저사례)
    if cur_cnt == K:
        for q in cur_list[1:]:
            print(q)
        return True

    # 순서대로 들어가기 때문에 cur_list에 있는 번호가 가장 큰 멤버는 -1의 인덱스에 있다.
    # 대신 빈 리스트에 -1을 부르면 에러가 뜨기 때문에 이 함수 호출 전에 반복문으로 0을 넣어주고 시작한다.
    for q in range(cur_list[-1] + 1, N + 1):
        if is_friend(q, cur_list):
            cur_list.append(q)
            if find_friends(cur_cnt + 1, cur_list):
                return True
            cur_list.pop()
    return False


if not find_friends(0, [0]):
    print(-1)

이 문제의 전체코드이다.

import sys

K, N, F = map(int, sys.stdin.readline().split())

friend = {i+1: [] for i in range(N)}

for i in range(F):
    first, second = map(int, sys.stdin.readline().split())
    friend[first].append(second)
    friend[second].append(first)


def judge(plus_friend, members):
    for per in members:
        if per not in friend[plus_friend]:
            return False
    return True


def backtracking(per_cnt, cur_members):
    if per_cnt == K:
        for i in cur_members:
            print(i)
        exit(0)

    for i in range(cur_members[-1] + 1, N + 1):
        if judge(i, cur_members):
            backtracking(per_cnt + 1, cur_members + [i])

for i in range(1, N):
    backtracking(1, [i])

print(-1)

전에 풀었던 코드인데 재귀함수를 빠져나올 방법을 찾지 못해서 그냥 exit로 프로그램을 종료해버렸었다.

물론 이렇게 풀어도 되기는 하지만 재귀함수, 백트래킹을 제대로 사용하기 위해 함수를 빠져 나올 수 있는 방법을 생각하며 풀어야겠다.

'알고리즘 > 브루트포스' 카테고리의 다른 글

백준 4675 셀프 넘버  (1) 2024.01.15
백준 1065 한수  (0) 2024.01.15
게임판 덮기  (0) 2022.09.17
보글 게임  (1) 2022.09.16
조합을 구하는 알고리즘  (0) 2022.09.15
728x90

 

 

algospot.com :: BOGGLE

보글 게임 문제 정보 문제 보글(Boggle) 게임은 그림 (a)와 같은 5x5 크기의 알파벳 격자인 게임판의 한 글자에서 시작해서 펜을 움직이면서 만나는 글자를 그 순서대로 나열하여 만들어지는 영어

algospot.com

 

 

GitHub - Seungkyu-Han/PS_strategy: 알고리즘 분류하는 워크플레이스

알고리즘 분류하는 워크플레이스. Contribute to Seungkyu-Han/PS_strategy development by creating an account on GitHub.

github.com

이 보글게임 문제를 브루트포스, 재귀로 풀 수 있다. (그냥 알고리즘만 구현해보고 채점은 안해봐서 모르겠다... 그냥  참고만)

def hasword(cur_x, cur_y, target_word, visited):  # 해당 위치에 찾는 단어가 있는지 확인하는 함수
    # 범위를 확인하지 않고 넘어오기 때문에 범위 확인을 가장 먼저 해준다.
    if not (0 <= cur_x < 5 and 0 <= cur_y < 5):
        return False

    if len(target_word) == 0:
        return True

    if target_word[0] != board[cur_x][cur_y]:
        return False

    for t in range(8):
        go_x = cur_x + dx[t]
        go_y = cur_y + dy[t]

        if not (0 <= go_x < 5 and 0 <= go_y < 5):
            continue

        if not visited[go_x][go_y]:
            continue

        visited[go_x][go_y] = False
        if hasword(go_x, go_y, target_word[1:], visited):
            return True
        visited[go_x][go_y] = True

    return False

이 알고리즘에서 가장 중요한 함수이다.

이 문제를 해결하기 위해서는

현재 위치(cur_x, cur_y)에 첫 글자가 있는가?

주위의 8칸 중, 그 위치에서 시작해서 단어의 나머지 글자들을 찾을 수 있는가? 를 알아야 한다.

여기서 현재 위치에 첫 글자가 있으면, 주위 칸으로 가서 두번째 문자부터 시작하면 된다.

이 일을 문자가 없어질 때까지 반복한다. (target_len의 길이 == 0이 종료조건)

이렇게 문제를 부분 문제로 나누어서 풀 수 있어야 한다. (솔직히 떠올리기가 어려운 것 같다.)

 

이 알고리즘을 구현하면서 visited를 사용해 한 글자를 두 번 이상 사용하지 못하게 만들었다.

N = int(input('단어의 수를 입력 : '))

words = []

for i in range(N):
    words.append(list(input().strip()))

print('보드를 입력')

board = []

for i in range(5):
    board.append(list(input().strip()))

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


def hasword(cur_x, cur_y, target_word, visited):  # 해당 위치에 찾는 단어가 있는지 확인하는 함수
    # 범위를 확인하지 않고 넘어오기 때문에 범위 확인을 가장 먼저 해준다.
    if not (0 <= cur_x < 5 and 0 <= cur_y < 5):
        return False

    if len(target_word) == 0:
        return True

    if target_word[0] != board[cur_x][cur_y]:
        return False

    for t in range(8):
        go_x = cur_x + dx[t]
        go_y = cur_y + dy[t]

        if not (0 <= go_x < 5 and 0 <= go_y < 5):
            continue

        if not visited[go_x][go_y]:
            continue

        visited[go_x][go_y] = False
        if hasword(go_x, go_y, target_word[1:], visited):
            return True
        visited[go_x][go_y] = True

    return False


def solve(target_word):
    for q in range(5):
        for w in range(5):
            visited_list = [[True] * 5 for i in range(5)]
            if hasword(q, w, target_word, visited_list):
                return True
    return False


for i in range(N):
    if solve(words[i]):
        print(*words[i])

위 함수를 포함한 전체 코드이다.

단어가 많아진다면 트라이를 통해서 더 빠르게 구현할 수 있겠지만 난이도는 많이 높아질 것 같다....

'알고리즘 > 브루트포스' 카테고리의 다른 글

백준 4675 셀프 넘버  (1) 2024.01.15
백준 1065 한수  (0) 2024.01.15
게임판 덮기  (0) 2022.09.17
소풍  (1) 2022.09.16
조합을 구하는 알고리즘  (0) 2022.09.15
728x90
 

GitHub - Seungkyu-Han/PS_strategy: 알고리즘 분류하는 워크플레이스

알고리즘 분류하는 워크플레이스. Contribute to Seungkyu-Han/PS_strategy development by creating an account on GitHub.

github.com

브루트포스와 재귀를 이용하여 n개의 원소 중에서 m개를 고르는 조합을 찾는 알고리즘이다.

def pick(cur, cur_list):
    if cur == n:
        print(*cur_list[1:])
        return

    for i in range(cur_list[-1] + 1, m + 1):
        cur_list.append(i)
        pick(cur + 1, cur_list)
        cur_list.pop(-1)

 

함수를 사용했고 cur가 현재 선택한 원소의 수, cur_list가 현재 선택한 원소의 배열이다.

 

여기서 수를 작은 수부터 차례로 넣었기 때문에 다음에 추가할 수는 cur_list[-1]보다 큰 수부터 보면 된다.

(현재 함수에서 추가할 수는 cur_list[-1]보다 1 큰 수 부터 m까지만 보면 된다는 말)

 

반복문을 이용하여 수를 넣어주고, 그 배열을 바탕으로 함수를 호출하고, 배열에 들어간 수를 다시 빼주고를 반복한다.

브루투포스로 분류하기는 했지만 백트래킹 알고리즘이기도 하다. (코드를 조금만 수정하면 백준 15650의 답이다.)

 

해당함수를 바탕으로 작성한 전체 코드이다.

함수가 현재 배열에 있는 가장 마지막 값을 기준으로 작동하기 때문에 배열에 0을 넣어주고 시작한다.

출력시에는 1번 인덱스부터 출력할 수 있도록 한다.

# m개 중에서 n개를 고르는 모든 조합을 찾는 알고리즘, 완전탐색

import sys

print('n과 m을 입력 : ', end='')
n, m = map(int, sys.stdin.readline().split())


def pick(cur, cur_list):
    if cur == n:
        print(*cur_list[1:])
        return

    for i in range(cur_list[-1] + 1, m + 1):
        cur_list.append(i)
        pick(cur + 1, cur_list)
        cur_list.pop(-1)


pick(0, [0])

'알고리즘 > 브루트포스' 카테고리의 다른 글

백준 4675 셀프 넘버  (1) 2024.01.15
백준 1065 한수  (0) 2024.01.15
게임판 덮기  (0) 2022.09.17
소풍  (1) 2022.09.16
보글 게임  (1) 2022.09.16
728x90

백준 9663 N-Queen문제이다.

 

전에 올린 백준 1799 비숍 문제와 유사하지만 조금 더 쉽다.

사용하는 방법은 백트래킹이다.

 

가로, 세로 한 줄에는 하나의 퀸 밖에 들어가지 못하니 한 줄을 기준으로 정하고 하나씩 더해주면 된다.(물론 불가능한 경우는 백트래킹으로 걸러주어야 한다.)

 

여기서는 가로로 한 줄씩 더해주었다.

이 문제에서 조금 귀찮은 부분은 대각선으로 불가능한 경우를 찾아서 제거해주는 것인데, abs(row[x]-row[i]) == abs(x - i)로 찾아주면 된다. (가로, 세로의 차이가 같다면 대각선에 있다)

 

최종코드는 

n = int(input())

ans = 0
row = [0] * n


def is_possible(x):
    for i in range(x):
        if row[x] == row[i] or abs(row[x] - row[i]) == abs(x - i):
            return False

    return True


def n_queens(x):
    global ans
    if x == n:
        ans += 1
        return

    else:
        for i in range(n):
            # [x, i]에 퀸을 놓겠다.
            row[x] = i
            if is_possible(x):
                n_queens(x + 1)


n_queens(0)
print(ans)

아쉽지만 pypy3로 제출할 수 있도록 하자.

'알고리즘 > 백트래킹' 카테고리의 다른 글

백준 1799 비숍  (0) 2022.09.05
728x90

백준 1799 비숍 문제이다.

백준 9663 N-Queen 문제와 유사하다.

만약 문제가 풀리지 않는다면 비교적 난이도가 쉬운 N-Queen(같은 백트래킹 문제)을 먼저 풀고 오기를 바란다.

처음에 풀었던 방식은 비숍의 모든 좌표들을 구해서 하나씩 넣어가며 가능여부를 판단하는 백트래킹이었다.

import sys

N = int(sys.stdin.readline())

board = []
res = 0

for i in range(N):
    board.append(list(map(int, sys.stdin.readline().split())))

left_down = [[] for i in range(2 * N - 1)]

for i in range(N):
    for t in range(N):
        if board[i][t] == 1:
            left_down[i+t].append((i, t))


def solve(cur_list, n):
    if n == 2 * N - 1:
        global res
        res = max(res, len(cur_list))
        return

    for next in left_down[n]:
        flag = True
        for_x = N - next[0] - 1
        for_y = next[1]
        for x, y in cur_list:
            if for_x + for_y == (N - x - 1) + y:
                flag = False
                break
        if flag:
            solve(cur_list + [next], n + 1)

    solve(cur_list, n + 1)

solve([], 0)
print(res)

이렇게 하면 시간초과가 나온다. (솔직히 시간초과 나올거 알고 있었지만 귀찮아서 이것부터 해보았다, 이러면 오히려 더 오래 걸린다 반성하자)

그 다음으로 사용한 방법은

줄을 이렇게 대각선으로 나누고 각각의 줄에 비숍이 있다면 하나씩 빼오는 것이었다 (한 줄에는 하나의 비숍만 들어갈 수 있으니, 당연히 기존의 비숍들과 계산하여 백트래킹을 사용했다.)

 

이렇게 만들어진 코드는

import sys

N = int(sys.stdin.readline())

board = []
res = 0

for i in range(N):
    board.append(list(map(int, sys.stdin.readline().split())))

left_down = [[] for i in range(2 * N - 1)]
right_down = []

for i in range(N):
    for t in range(N):
        if board[i][t] == 1:
            left_down[i + t].append((i, t))


def solve(cur_cnt, avoid, n):
    global res
    if cur_cnt + (2 * N - 1 - n) < res:
        return

    if n == 2 * N - 1:
        res = max(res, cur_cnt)
        return

    for next in left_down[n]:
        for_x = next[0]
        for_y = N - next[1] - 1
        if avoid[for_x + for_y]:
            avoid[for_x + for_y] = False
            solve(cur_cnt + 1, avoid, n + 1)
            avoid[for_x + for_y] = True
    solve(cur_cnt, avoid, n + 1)


solve(0, [True] * (2 * N - 1), 0)
print(res)

이렇게만 하면 맞을 수 있을 줄 알았다.

유사한 N-Queens 문제에서는 여기까지만 하면 맞출 수 있따.

하지만 이 코드도 시간초과가 나온다.

 

이 문제를 맞추기 위해서는 흰색칸의 비숍과, 검은색칸의 비숍을 분리해서 최댓값을 구한 후 더해주어야 한다.

(색이 다른 칸의 비숍들은 서로 잡을 수 없기 때문)

import sys

N = int(sys.stdin.readline())

board = []
left_down = [[] for i in range(2 * N - 1)]
odd_max = 0
even_max = 0

for i in range(N):
    board.append(list(map(int, sys.stdin.readline().split())))

for i in range(N):
    for t in range(N):
        if board[i][t] == 1:
            left_down[i + t].append((i, t))


def solve(cur_cnt, avoid, n):
    global odd_max, even_max

    if n >= 2 * N - 1:
        if n % 2 == 0:
            even_max = max(even_max, cur_cnt)
        else:
            odd_max = max(odd_max, cur_cnt)
        return

    for next in left_down[n]:
        for_x = next[0]
        for_y = N - next[1] - 1
        if avoid[for_x + for_y]:
            avoid[for_x + for_y] = False
            solve(cur_cnt + 1, avoid, n + 2)
            avoid[for_x + for_y] = True
    solve(cur_cnt, avoid, n + 2)


solve(0, [True] * (2 * N - 1), 0)
solve(0, [True] * (2 * N - 1), 1)

print(even_max + odd_max)

홀수 줄의 비숍, 짝수 줄의 비숍의 최댓값을 구한 후 각각 더했고 그 과정에서 사용한 코드는 위와 동일하다.

'알고리즘 > 백트래킹' 카테고리의 다른 글

백준 9663 N-Queen  (0) 2022.09.06

+ Recent posts