DFS(깊이 우선 탐색)와 BFS(너비 우선 탐색)
각 그래프의 각 정점을 방문하는 그래프 순회(Graph Traversals)에는 크게 깊이 우선 탐색(Depth-First Search) 와 너비 우선 탐색(Breadth-First Search) 의 두 가지 알고리즘이 있습니다.DFS는 주로 스택이나 재귀로 구현하고, 백트래킹을 통해 효용이 뛰어납니다.코딩 테스트 대부분의 그래프 탐색은 DFS로 구현하게 된다고 합니다.BFS는 주로 큐로 구현하며, 그래프의 최단 경로를 구하는 문제에 사용됩니다.다익스트라 알고리즘 등탐색할 그래프의 예시 형태graph = { 1: [2, 3, 4], 2: [5], 3: [5], 4: [], 5: [6, 7], 6: [], 7: [3],}DFS (깊이 우선 탐색)재귀 구조 DFS위키피디..
2025.04.21
no image
BOJ 16439 치킨치킨치킨
백준 BOJ 16439 치킨치킨치킨 문제의 파이썬 python 풀이입니다.https://www.acmicpc.net/problem/16439문제N명의 고리 회원들은 치킨을 주문하고자 합니다.치킨은 총 M가지 종류가 있고 회원마다 특정 치킨의 선호도가 있습니다. 한 사람의 만족도는 시킨 치킨 중에서 선호도가 가장 큰 값으로 결정됩니다. 진수는 회원들의 만족도의 합이 최대가 되도록 치킨을 주문하고자 합니다.시키는 치킨의 종류가 많아질수록 치킨을 튀기는 데에 걸리는 시간도 길어지기 때문에 최대 세 가지 종류의 치킨만 시키고자 합니다.진수를 도와 가능한 만족도의 합의 최댓값을 구해주세요. 입력첫 번째 줄에 고리 회원의 수 N (1 ≤ N ≤ 30) 과 치킨 종류의 수 M (3 ≤ M ≤ 30) 이 주어집니다..
2025.04.19
no image
BOJ 23253 자료구조는 정말 최고야
백준 BOJ 23253 자료구조는 정말 최고야 문제의 파이썬 python 풀이입니다.https://www.acmicpc.net/problem/23253문제찬우는 스택을 배운 뒤 자료구조 과목과 사랑에 빠지고 말았다.자료구조 과목만을 바라보기로 다짐한 찬우는 나머지 과목의 교과서 $N$권을 방 구석에 $M$개의 더미로 아무렇게나 쌓아 두었다. 하지만 중간고사가 다가오자 더 이상 자료구조만 공부할 수는 없었고, 결국 찬우는 팽개쳤던 나머지 과목의 교과서를 정리하고 번호순으로 나열하려 한다. N$N$권의 교과서는 각각 $1$부터 $N$까지의 번호가 매겨져 있다. 찬우는 각 더미의 맨 위에 있는 교과서만 꺼낼 수 있으며, 반드시 교과서를 꺼낸 순서대로 나열해야 하기 때문에 번호순으로 나열하기 위해서는 $1$번,..
2025.04.17
no image
LeetCode 46 Permutations
리트코드 LeetCode 46 Permutations 문제의 파이썬 python 풀이입니다.https://leetcode.com/problems/permutations/문제2에서 9까지 숫자가 주어졌을 때 전화 번호로 조합 가능한 모든 문자를 출력하라.풀이DFS를 활용한 정석적인 풀이 방법과, 파이썬 itertools 모듈을 사용한 편리한 풀이 방법 두 가지를 작성했습니다. 1. DFS를 활용한 순열 생성# 순열 : DFSclass Solution: def permute(self, nums: List[int]) -> List[List[int]]: result = [] # 최종 순열 결과를 저장하는 리스트 prev_elements = [] # 지금까지 선택한 숫자들을 저장 (..
2025.04.12
no image
LeetCode 17 Letter Combinations of a Phone Number
리트코드 LeetCode 17 Letter Combinations of a Phone Number 문제의 파이썬 python 풀이입니다. https://leetcode.com/problems/letter-combinations-of-a-phone-number/description/문제2에서 9까지 숫자가 주어졌을 때 전화 번호로 조합 가능한 모든 문자를 출력하라.풀이# 폰 번호 문자 조합 : 모든 조합 탐색 (DFS)class Solution: def letterCombinations(self, digits: str) -> List[str]: def dfs(index, path): # 끝까지 탐색하면 백트래킹 if len(path) == len(d..
2025.04.05
no image
Object Detection Wrap-Up
Object Detection 개인적으로 헷갈리기 쉬운 내용들🔑 Detector 구조의 구성 요소, loss, evaluation metric, Precision & Recall, Corner Pooling, DarkNet Output, AP 계산하기Detector 구조의 구성 요소CornerNetAnchor-free 방식의 one-stage detectorRPN을 사용하지 않음[Input → Backbone → Dense prediction] 구조Fast R-CNNRPN이 없음 (selective search 사용)Neck 구조가 없음[Input → Backbone → Sparse prediction] 구조YOLOOne-stage detector로 RPN 없음[Input → Backbone → Den..
2025.04.05
no image
BOJ 16961 탭 vs 공백
백준 BOJ 16961 탭 vs 공백 문제의 파이썬 python 풀이입니다.https://www.acmicpc.net/problem/16961문제큐브러버가 운영하는 호텔은 1년 스케줄을 전년도에 미리 정해놓는다. 또, 이 호텔에는 코딩을 할 줄 아는 사람만 투숙할 수 있다. 코딩을 할 때 들여쓰기는 필수이다. 호텔에는 두 종류의 사람이 투숙하는데, 들여쓰기로 탭을 사용하는 사람과 공백을 사용하는 사람이다. 편의를 위해 각각 "탭파"와 "공백파"로 부르도록 하자.올해는 윤년이기 때문에, 총 366일로 이루어져 있다. 편의상 날짜는 1일-366일로 표시한다.큐브러버의 호텔에 예약을 한 사람의 수는 N명이고, 각 사람이 투숙 시작일과 종료일을 모두 알고 있다. 모든 사람은 투숙 시작일의 오전 9시에 호텔에 투..
2025.04.05
no image
Advanced Object Detection
🔑 Cascade RCNN, ViT, YOLO, M2Det, CornerNetCascade RCNNCascade RCNN은 학습할 bounding box의 IOU threshold를 0.5에서 더 높여보면 어떨까라는 아이디어에서 출발했음. Cascade RCNN은 Iterative BBox at Inference와 Intergral Loss를 결합하여 기존의 Faster RCNN보다 더 좋은 성능을 나타낸다Cascade RCNN은 Iterative BBox at Inference와 Intergral Loss를 사용해서 Bbox pooling을 반복 수행할 시 성능 향상되는 것, IOU threshold가 다른 Classifier가 반복될 때 성능 향상 되는 것을, IOU threshold가 다른 Ro..
2025.04.02

각 그래프의 각 정점을 방문하는 그래프 순회(Graph Traversals)에는 크게 깊이 우선 탐색(Depth-First Search)너비 우선 탐색(Breadth-First Search) 의 두 가지 알고리즘이 있습니다.

  • DFS는 주로 스택이나 재귀로 구현하고, 백트래킹을 통해 효용이 뛰어납니다.
    • 코딩 테스트 대부분의 그래프 탐색은 DFS로 구현하게 된다고 합니다.
  • BFS는 주로 큐로 구현하며, 그래프의 최단 경로를 구하는 문제에 사용됩니다.
    • 다익스트라 알고리즘 등

탐색할 그래프의 예시 형태

graph = {
    1: [2, 3, 4],
    2: [5],
    3: [5],
    4: [],
    5: [6, 7],
    6: [],
    7: [3],
}





DFS (깊이 우선 탐색)

재귀 구조 DFS

위키피디아의 수도코드에는, 정점 v의 모든 인접 유향 간선들을 반복하라고 표기되어 있습니다. 이를 파이썬 코드로 구현해보면 다음과 같습니다.

def recursive_dfs(v, discovered=[]):
    discovered.append(v) # v를 방문 표시
    for w in graph[v]: # 노드 v와 연결된 이웃 노드들을 순회하면서 방문하지 않은 노드들이 있는지 확인
            if w not in discovered:
                discovered = recursive_dfs(w, discovered)
    return discovered
  • 방문했던 정점(discovered)를 계속 누적된 결과로 만들기 위해 리턴하는 형태만 받아오도록 처리했습니다.
  • 한 노드를 방문하면 그 노드의 이웃 중 아직 방문하지 않은 노드를 계속해서 들어가면서 탐색하고, 더 이상 갈 수 없을 때 다시 돌아오는 구조입니다.

위 코드에서 주의할 점은 discovered=[] 기본 인자인데, 파이썬에서 기본 인자로 변경 가능한 객체(리스트나 딕셔너리 등) 를 설정하면, 의도치 않게 함수 호출 간 값이 공유될 수 있어 문제가 발생할 수 있습니다. 그렇게 때문에 아래와 같은 방식으로 사용하면 문제를 예방할 수 있습니다.

def recursive_dfs(v, discovered=None):
    if discovered is None:
        discovered = []
    discovered.append(v)
    for w in graph[v]:
        if w not in discovered:
            recursive_dfs(w, discovered)

    return discovered

스택을 이용한 반복 구조 DFS

def iterative_dfs(start_v):
    discovered = []
    stack = [start_v] # 탐색 시작할 노드, 초기 스택에는 시작 노드만 넣음
    while stack: # 스택이 비어있지 않다면 반복
        v = stack.pop() # 스택에서 하나 꺼내서 v로 저장
        if v not in discovered:
            discovered.append(v) # 방문하지 않은 v라면 방문 처리
            for w in graph[v]:
                stack.append(w) # 인접 노드들을 모두 스택에 추가

    return discovered
  • 실행 속도는 재귀에 비해 더 빠른 편입니다.
  • 앞선 재귀 DFS는 사전식 순서로 방문했는데, 반복 DFS는 역순으로 방문했습니다.
    • 스택으로 구현하다 보니 가장 마지막에 삽입된 노드부터 꺼내서 반복하게 되고 이 경우 인접 노드에 가장 최근에 담긴 노드, 즉 가장 마지막부터 방문하기 때문입니다.





BFS (너비 우선 탐색)

큐를 이용한 반복 구조 BFS

스택을 이용하는 DFS와 달리 BFS는 큐를 이용합니다.
모든 인접 간선을 추출하고 도착점인 정점을 큐에 삽입하는 구조입니다.

def iterative_bfs(start_v):
    discovered = [start_v] # 시작 노드는 방문했으므로 넣고 시작
    queue = [start_v]
    while queue: # 큐가 빌 때까지 반복
        v = queue.pop(0)
        for w in graph[v]:
            if w not in discovered:
                discovered.append(w)  # 방문하지 않은 w라면 방문 처리
                queue.append(w)       # 인접 노드를 큐에 추가
    return discovered

최적화를 위해 데크 같은 자료형을 사용하는 것도 고민해볼 수 있겠습니다.

재귀를 이용한 BFS?

  • BFS는 재귀로 동작하지 않습니다.
  • 큐를 활용한 반복 구조로만 구현이 가능하기 때문에, 코딩 테스트 풀이에서 혼동해서는 안 되겠습니.





스택 버전 DFS와 큐 버전 BFS의 차이

# DFS: Stack을 사용한 반복 깊이 우선 탐색
def iterative_dfs(start_v):
    discovered = []
    stack = [start_v]
    while stack:
        v = stack.pop()                      # ✅ 마지막에 넣은 노드를 꺼냄
        if v not in discovered:
            discovered.append(v)             # 방문 처리
            for w in graph[v]:
                stack.append(w)              # 인접 노드를 스택에 추가
    return discovered
# BFS: Queue를 사용한 반복 너비 우선 탐색
def iterative_bfs(start_v):
    discovered = [start_v]
    queue = [start_v]
    while queue:
        v = queue.pop(0)                     # ✅ 처음에 넣은 노드를 꺼냄
        for w in graph[v]:
            if w not in discovered:
                discovered.append(w)         # 방문 처리
                queue.append(w)              # 인접 노드를 큐에 추가
    return discovered

항목 DFS (Stack) BFS (Queue)
자료구조 stack = [start_v] queue = [start_v]
꺼내는 방식 v = stack.pop() → 마지막 요소 v = queue.pop(0) → 첫 번째 요소
넣는 대상 인접 노드 w뒤에서부터 스택에 쌓음 인접 노드 w순서대로 큐에 넣음
탐색 순서 깊게, 깊게 들어감 → 끝까지 갔다가 되돌아옴 넓게, 가까운 노드부터 모두 보고 나중에 멀리 감
방문 순서 예시 1 → 4 → 3 → 5 → 7 → 6 → 2 (깊이 우선) 1 → 2 → 3 → 4 → 5 → 6 → 7 (너비 우선)

  • DFS는 "가장 최근에 추가된 노드"부터 탐색
    → 스택(LIFO)이기 때문
  • BFS는 "가장 먼저 추가된 노드"부터 탐색
    → 큐(FIFO)이기 때문

그래서 코드는 거의 똑같은 구조지만, pop의 위치 차이(끝 vs 앞) 때문에 완전히 다른 탐색 결과가 나오게 됩니다.





백트래킹

백트래킹(Backtracking) 은 해결책에 대한 후보를 구축해 나아가다 가능성이 없다고 판단되는 즉시 후보를 포기하고 정답을 찾아 돌아가는 범용적인 알고리즘으로, 제약 충족 문제(Constraint Satisfaction Problems)에 특히 유용합니다.

  • 백트래킹은 DFS와 같은 방식으로 탐색하는 모든 방법을 뜻하며, DFS(깊이 우선 탐색) 는 백트래킹의 골격을 이루는 알고리즘입니다.
  • 백트래킹은 주로 재귀 로 구현하며, 모두 DFS의 범주에 속합니다.
  • 브루트 포스와 유사하지만, 가능성이 없는 경우 후보를 포기(트리의 가지치기, Prunning)한다는 점에서 좀 더 효율적이라고 할 수 있겠습니다.




백준 BOJ 16439 치킨치킨치킨 문제의 파이썬 python 풀이입니다.

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



문제

N명의 고리 회원들은 치킨을 주문하고자 합니다.

치킨은 총 M가지 종류가 있고 회원마다 특정 치킨의 선호도가 있습니다. 한 사람의 만족도는 시킨 치킨 중에서 선호도가 가장 큰 값으로 결정됩니다. 진수는 회원들의 만족도의 합이 최대가 되도록 치킨을 주문하고자 합니다.

시키는 치킨의 종류가 많아질수록 치킨을 튀기는 데에 걸리는 시간도 길어지기 때문에 최대 세 가지 종류의 치킨만 시키고자 합니다.

진수를 도와 가능한 만족도의 합의 최댓값을 구해주세요.




입력

첫 번째 줄에 고리 회원의 수 N (1 ≤ N ≤ 30) 과 치킨 종류의 수 M (3 ≤ M ≤ 30) 이 주어집니다.

두 번째 줄부터 N개의 줄에 각 회원의 치킨 선호도가 주어집니다.

i+1번째 줄에는 i번째 회원의 선호도가 주어집니다.




출력

첫 번째 줄에 고리 회원들의 만족도의 합의 최댓값을 출력합니다.




접근 : itertools.combinations

정석적인 풀이 방법은 치킨의 종류 M개 중에서 3개를 고르는 조합을 브루트포스로 전부 시도해 보는 것일 겁니다.

  • combinations를 활용하면 좋겠습니다.


itertools.combinations

itertools 모듈의 combinations(iterable, r) 함수는

  • 주어진 iterable에서 r개를 뽑는 모든 조합을 만들어 줍니다.
  • 순서 상관없이 중복 없이 조합을 만듭니다.
  • 반환되는 것은 튜플들의 이터레이터

사용 예시

from itertools import combinations

for comb in combinations(\[1, 2, 3, 4\], 2):  
print(comb)

출력 결과

(1, 2)
(1, 3)
(1, 4)
(2, 3)
(2, 4)
(3, 4)
  • 4개의 원소 중에서 2개씩 뽑는 모든 조합을 출력한 것!




풀이

# 16439 치킨치킨치킨

import sys
from itertools import combinations

input = sys.stdin.readline

n, m = map(int, input().split())

# 선호도 정보 저장
likes = [list(map(int, input().split())) for _ in range(n)]

max_total = 0

# 치킨 종류 중 3개 고르는 모든 조합
for comb in combinations(range(m), 3):
    total = 0
    for i in range(n):
        # 회원 i의 선호도 중 선택된 3개 치킨에 대한 점수 중 최고값
        max_like = max(likes[i][comb[0]], likes[i][comb[1]], likes[i][comb[2]])
        total += max_like
    max_total = max(max_total, total)

print(max_total)
  • likes [1] [3]은 2번째 회원이 4번째 치킨에 대해 매긴 선호 점수에 해당할 것
  • combinations(range(m), 3) → 치킨 종류 중 3개 고르기
  • 회원별로 3개 치킨 중 최고 선호도만 선택해서 총합 계산
  • 모든 조합을 시도해서 최대값을 찾음



'Algorithm > Python' 카테고리의 다른 글

BOJ 23253 자료구조는 정말 최고야  (0) 2025.04.17
LeetCode 46 Permutations  (0) 2025.04.12
LeetCode 17 Letter Combinations of a Phone Number  (0) 2025.04.05
BOJ 16961 탭 vs 공백  (0) 2025.04.05
BOJ 27162 Yacht Dice  (0) 2025.03.30

백준 BOJ 23253 자료구조는 정말 최고야 문제의 파이썬 python 풀이입니다.

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



문제

찬우는 스택을 배운 뒤 자료구조 과목과 사랑에 빠지고 말았다.

자료구조 과목만을 바라보기로 다짐한 찬우는 나머지 과목의 교과서 $N$권을 방 구석에 $M$개의 더미로 아무렇게나 쌓아 두었다. 하지만 중간고사가 다가오자 더 이상 자료구조만 공부할 수는 없었고, 결국 찬우는 팽개쳤던 나머지 과목의 교과서를 정리하고 번호순으로 나열하려 한다.

 N$N$권의 교과서는 각각 $1$부터 $N$까지의 번호가 매겨져 있다. 찬우는 각 더미의 맨 위에 있는 교과서만 꺼낼 수 있으며, 반드시 교과서를 꺼낸 순서대로 나열해야 하기 때문에 번호순으로 나열하기 위해서는 $1$번, $2$번, … $N - 1$번, $N$번 교과서 순으로 꺼내야 한다. 교과서를 올바르게 나열할 수 없다면 중간고사 공부를 때려치겠다는 찬우를 위해 번호순으로 나열할 수 있는지 여부를 알려주는 프로그램을 작성해 주자.



입력

첫째 줄에 교과서의 수 $N$, 교과서 더미의 수 $M$이 주어진다.

둘째 줄부터 $2\times M$줄에 걸쳐 각 더미의 정보가 주어진다.

 $i$번째 더미를 나타내는 첫 번째 줄에는 더미에 쌓인 교과서의 수 $k_{i}$ 가 주어지며, 두 번째 줄에는 $k_{i}$ 개의 정수가 공백으로 구분되어 주어진다.

각 정수는 교과서의 번호를 나타내며, 아래에 있는 교과서의 번호부터 주어진다.

교과서의 번호는 $1$부터 $N$까지의 정수가 한 번씩만 등장한다.



출력

올바른 순서대로 교과서를 꺼낼 수 있다면 Yes를, 불가능하다면 No를 출력한다.




풀이

먼저 시도했던 방법은 교과서 번호를 다 확인하면서 모든 더미를 순회하는 방식이었습니다.

시간 초과

# 23253 자료구조는 정말 최고야

import sys

# 교과서의 수 n, 교과서 더미의 수 m
n, m = map(int, sys.stdin.readline().split())

books = []

for _ in range(m):
    # 더미에 쌓인 교과서 수 k
    k = int(sys.stdin.readline())
    books.append(list(map(int, sys.stdin.readline().split())))

i = 1
while i <= n:
    for book in books:
        if book and book[-1] == i:
            i += 1
            book.pop()
            break

    else:
        print("No")
        exit()

print("Yes")
  • 교과서 번호 1 ~ N까지 순서대로 하나씩 확인하는 것을, 매번 M개의 더미 모두 순회하기 때문에 최악의 경우 O(N * M) 시간 복잡도가 발생합니다.

스택 특성, top 활용

모든 스택이 내림차순으로만 쌓여 있어야 전체적으로 순서대로 꺼낼 수 있습니다.
즉, 각 스택에서 book[i] > book[i+1] 이어야 합니다.

# 23253 자료구조는 정말 최고야

import sys

n, m = map(int, sys.stdin.readline().split())

for _ in range(m):
    k = int(sys.stdin.readline())
    stack = list(map(int, sys.stdin.readline().split()))
    # 위에서부터 아래로 검사 (책 쌓인 순서대로)
    for i in range(k - 1):
        if stack[i] < stack[i + 1]:
            print("No")
            exit()

print("Yes")
  • 스택 특성상 위에서부터 꺼내기 때문에, 스택 내부는 반드시 내림차순이어야만 전체적으로 1 → N 순서대로 뺄 수 있습니다
  • 이 조건만 만족하면 순서대로 뽑는 것이 항상 가능하므로, 모든 스택이 내림차순인지 확인만 하면 됨!
  • 이렇게 하면 스택 하나당 O(k) 검사만 하면 되기 때문에 전체 시간 복잡도는 O(N) (k₁ + k₂ + ... + kₘ = N)




'Algorithm > Python' 카테고리의 다른 글

BOJ 16439 치킨치킨치킨  (0) 2025.04.19
LeetCode 46 Permutations  (0) 2025.04.12
LeetCode 17 Letter Combinations of a Phone Number  (0) 2025.04.05
BOJ 16961 탭 vs 공백  (0) 2025.04.05
BOJ 27162 Yacht Dice  (0) 2025.03.30

 

리트코드 LeetCode 46 Permutations 문제의 파이썬 python 풀이입니다.

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



문제

2에서 9까지 숫자가 주어졌을 때 전화 번호로 조합 가능한 모든 문자를 출력하라.



풀이

DFS를 활용한 정석적인 풀이 방법과, 파이썬 itertools 모듈을 사용한 편리한 풀이 방법 두 가지를 작성했습니다.

 

1. DFS를 활용한 순열 생성

# 순열 : DFS

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

        result = [] # 최종 순열 결과를 저장하는 리스트
        prev_elements = [] # 지금까지 선택한 숫자들을 저장 (경로)

        def dfs(elements): # 현재 사용할 수 있는 숫자들(elements)를 입력으로 받음
            # 더 이상 선택할 수 없는 숫자가 없는 리프 노드일 때 -> 하나의 순열이 완성된 것
            if len(elements) == 0:
                result.append(prev_elements[:]) # 현재까지 선택된 숫자 경로(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 result
class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
  • permute 함수는 nums라는 정수 리스트를 받아서, 가능한 모든 순열을 리스트 형태로 반환합니다.
  • 예: nums = [1, 2, 3] → 결과는 [[1,2,3],[1,3,2],[2,1,3],...[3,2,1]]

 

DFS 재귀 호출

for e in elements:
    next_elements = elements[:]
    next_elements.remove(e)

    prev_elements.append(e)
    dfs(next_elements)
    prev_elements.pop()
  • 현재 선택할 수 있는 숫자들 중 하나(e)를 고릅니다.
  • e를 선택한 후엔 더 이상 해당 숫자를 선택할 수 없으므로 elements에서 제거한 새 리스트(next_elements)를 만듭니다.
  • 그 숫자를 현재 경로(prev_elements)에 추가하고 나머지 숫자들을 대상으로 DFS를 재귀적으로 실행합니다.
  • 재귀 호출 이후에는 prev_elements에서 마지막에 추가한 숫자를 제거하여 백트래킹합니다.

 

진행 과정

입력: nums = [1, 2, 3]

dfs([1, 2, 3])
 └─ 1 선택 → prev = [1], elements = [2, 3]
     └─ 2 선택 → prev = [1, 2], elements = [3]
         └─ 3 선택 → prev = [1, 2, 3], elements = []
             └─ 저장! [1, 2, 3]
         ← 3 제거 → prev = [1, 2]
     ← 2 제거 → prev = [1]
     └─ 3 선택 → prev = [1, 3], elements = [2]
         └─ 2 선택 → prev = [1, 3, 2], elements = []
             └─ 저장! [1, 3, 2]
         ← 2 제거 → prev = [1, 3]
     ← 3 제거 → prev = [1]
 ← 1 제거 → prev = []

 └─ 2 선택 → prev = [2], elements = [1, 3]
     └─ 1 선택 → prev = [2, 1], elements = [3]
         └─ 3 선택 → prev = [2, 1, 3], elements = []
             └─ 저장! [2, 1, 3]
         ← 3 제거
     ← 1 제거
     └─ 3 선택 → prev = [2, 3], elements = [1]
         └─ 1 선택 → prev = [2, 3, 1], elements = []
             └─ 저장! [2, 3, 1]
         ← 1 제거
     ← 3 제거
 ← 2 제거

 └─ 3 선택 → prev = [3], elements = [1, 2]
     └─ 1 선택 → prev = [3, 1], elements = [2]
         └─ 2 선택 → prev = [3, 1, 2], elements = []
             └─ 저장! [3, 1, 2]
         ← 2 제거
     ← 1 제거
     └─ 2 선택 → prev = [3, 2], elements = [1]
         └─ 1 선택 → prev = [3, 2, 1], elements = []
             └─ 저장! [3, 2, 1]
         ← 1 제거
     ← 2 제거
 ← 3 제거

 

  • prev_elements: 지금까지의 선택 경로를 저장하는 리스트
  • elements: 현재 선택 가능한 후보 숫자들
  • dfs는 한 단계 깊이 들어가며 한 숫자를 고르고, 그 숫자를 후보에서 제거
  • 숫자를 다 골라서 elements == []가 되면 → 하나의 완성된 순열이므로 저장
  • 그런 다음 prev_elements.pop()을 통해 방금 선택한 숫자를 되돌리고, 다음 후보를 탐색

 

이 코드는 백트래킹 기법을 사용하여 nums의 모든 순열을 구합니다.

재귀 호출을 통해 하나씩 선택하고, 선택이 완료되면 결과에 저장하며, 선택을 되돌리며(백트래킹) 다른 경우의 수를 시도합니다.

시간복잡도는 O(n!)입니다. 가능한 모든 순열을 생성해야 하므로, n개의 숫자에 대해 n!개의 경우의 수가 생깁니다.




 

2. itertools 모듈 사용

파이썬에는 itertools라는 모듈이 있습니다. itertools 모듈은 반복자 생성에 최적화된 효율적인 기능들을 제공하므로, 실무에서는 알고리즘으로 직접 구현하기보다는 가능하다면 itertools 모듈을 사용하는 편이 낫겠습니다.

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        return list(map(list, itertools.permutations(nums)))

 

itertools.permutations() 함수의 결과물은 리스트 내 튜플입니다. 리트코드 문제에서는 리스트를 반환하도록 요구하기 떄문에, 리스트로 map 해주는 과정을 추가했습니다.



'Algorithm > Python' 카테고리의 다른 글

BOJ 16439 치킨치킨치킨  (0) 2025.04.19
BOJ 23253 자료구조는 정말 최고야  (0) 2025.04.17
LeetCode 17 Letter Combinations of a Phone Number  (0) 2025.04.05
BOJ 16961 탭 vs 공백  (0) 2025.04.05
BOJ 27162 Yacht Dice  (0) 2025.03.30

 

리트코드 LeetCode 17 Letter Combinations of a Phone Number 문제의 파이썬 python 풀이입니다.

 

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



문제

2에서 9까지 숫자가 주어졌을 때 전화 번호로 조합 가능한 모든 문자를 출력하라.



풀이

# 폰 번호 문자 조합 : 모든 조합 탐색 (DFS)

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) #path에 digits[i]을 더해서 가져감

        # 예외 처리
        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

 

def dfs(index, path):
  • index: 현재 몇 번째 숫자를 처리 중인지 나타냄
  • path: 지금까지 조합한 문자들을 저장하는 문자열
    예를 들어, '2' → abc, '3' → def이면
    • 첫 번째 단계에서 'a'를 선택 → path = 'a'
    • 두 번째 단계에서 'd'를 선택 → path = 'ad'
    • 끝까지 조합이 완료되면 → result.append('ad')

즉, path현재 만들고 있는 조합 중간 결과입니다.

 

예시

입력: digits = "23"
딕셔너리:

dic = {"2": "abc", "3": "def"}

DFS 흐름

dfs(0, ""):
  ├─ dfs(1, "a"):
  │   ├─ dfs(2, "ad") → 끝 → 저장
  │   ├─ dfs(2, "ae") → 끝 → 저장
  │   └─ dfs(2, "af") → 끝 → 저장
  ├─ dfs(1, "b"):
  │   ├─ dfs(2, "bd") → 끝 → 저장
  │   ├─ dfs(2, "be") → 끝 → 저장
  │   └─ dfs(2, "bf") → 끝 → 저장
  ├─ dfs(1, "c"):
      ├─ dfs(2, "cd") → 끝 → 저장
      ├─ dfs(2, "ce") → 끝 → 저장
      └─ dfs(2, "cf") → 끝 → 저장

 

재귀 호출을 하면서 이 문자열을 점점 길게 만들어 가다가, digits 길이와 같아지면 결과에 저장합니다.



'Algorithm > Python' 카테고리의 다른 글

BOJ 23253 자료구조는 정말 최고야  (0) 2025.04.17
LeetCode 46 Permutations  (0) 2025.04.12
BOJ 16961 탭 vs 공백  (0) 2025.04.05
BOJ 27162 Yacht Dice  (0) 2025.03.30
BOJ 25204 문자열 정렬  (0) 2025.03.30

Object Detection 개인적으로 헷갈리기 쉬운 내용들



Detector 구조의 구성 요소

  • CornerNet
    • Anchor-free 방식의 one-stage detector
    • RPN을 사용하지 않음
    • [Input → Backbone → Dense prediction] 구조
  • Fast R-CNN
    • RPN이 없음 (selective search 사용)
    • Neck 구조가 없음
    • [Input → Backbone → Sparse prediction] 구조
  • YOLO
    • One-stage detector로 RPN 없음
    • [Input → Backbone → Dense prediction] 구조
    • Sparse prediction 대신 Dense prediction 사용
  • Faster R-CNN + FPN
    • Input: 입력 이미지
    • Backbone: 특징 추출 (ResNet 등)
    • Neck: FPN (Feature Pyramid Network)
    • RPN: Region Proposal Network
    • Sparse prediction: RoI Head에서의 예측
    • 문제에서 제시한 5가지 구성요소를 모두 포함
  • RetinaNet
    • One-stage detector로 RPN 없음
    • [Input → Backbone → Neck → Dense prediction] 구조
    • Focal loss를 사용하는 것이 특징





YOLO v1 loss

  1. 첫 번째 부분
    • (x,y) 좌표와 (w,h) 크기에 대한 loss
    • 박스의 위치와 크기를 예측하는 localization loss
  2. 두 번째 부분
    • 객체가 있을 확률(C)에 대한 loss
    • confidence score를 예측하는 confidence loss
  3. 세 번째 부분
    • 클래스 확률(p)에 대한 loss
    • 객체의 클래스를 분류하는 classification loss

localization / confidence / classification

  • 수식의 순서와 정확히 일치
  • 각 loss의 역할을 정확히 반영





Object detection evaluation metric

  • FLOPS (Floating Point Operations Per Second)
    → Object detection에서 사용되는 metric
    • 모델의 연산량을 측정
    • 모델의 효율성 평가
    • 실제 구현 시 중요한 고려사항

  • FPS (Frames Per Second)
    → Object detection에서 사용되는 metric
    • 실시간 처리 속도 측정
    • 초당 처리할 수 있는 프레임 수
    • 실시간 응용에서 중요한 지표

  • FID (Fréchet Inception Distance)
    → Object detection metric이 아님
    • GAN 모델의 성능 평가에 사용되는 지표
    • 생성된 이미지의 품질 평가
    • Object detection과는 관련이 없음

  • mIoU (mean Intersection over Union)
    → Object detection에서 사용되는 metric
    • 예측 박스와 실제 박스의 겹침 정도 측정
    • 위치 예측의 정확도 평가
    • Segmentation에서도 사용됨

  • mAP (mean Average Precision)
    → Object detection에서 사용되는 metric임
    • 검출 정확도를 종합적으로 평가
    • PR 커브의 면적 계산
    • 가장 널리 사용되는 평가 지표





Precision & Recall

  • 먼저 각 요소를 파악
    • Ground truth (실제 고양이): 4마리
    • Positive prediction (빨간 박스): 4개
    • True Positive (TP): (실제 고양이를 맞게 예측)
    • False Positive (FP): (고양이가 아닌 것을 고양이로 예측)
    • False Negative (FN): (놓친 고양이)
  • 총 8개를 검출하고 그 중 3개가 옳은 검출이므로, precision = 3/8 = 0.375
  • 총 4개의 GT가 있고 그 중 3개를 검출했으므로, recall = 3/4 = 0.75





Corner Pooling

CornerNet에서는 corner를 결정하기 위해 corner pooling이라는 방법을 사용한다. 그림과 같은 두 개의 feature maps의 일부가 주어지고 각각에 대해 수직, 수평방향으로 pooling을 할 때, corner pooling의 output map에서 (a), (b), (c), (d) 에 들어갈 숫자의 순서로 옳은 것

Corner pooling은 각 feature map에 대해 horizontal pooling, vertical pooling을 수행한 후 element-wise summation을 수행한다.





DarkNet Output

YOLO v1에서는 입력 이미지를 SxS의 grid cell로 분할하며, 각 cell마다 B개의 bounding box와 confidence score, 그리고 각 cell마다 C개의 class에 대한 conditional class probabilities를 예측한다. Input image의 shape은 288x288x3이며 S=12, B=3, C=100일 경우, YOLO v1 DarkNet의 최종 output으로 나오는 feature map의 shape (W, H, C)이다. 이 때 W+H+C의 값으로 옳은 것

주어진 조건

  • Input image: 288x288x3
  • Grid cell: SxS (S=12)
  • 각 cell당 B개의 bounding box와 confidence score (B=3)
  • 각 cell당 C개의 클래스에 대한 확률 (C=100)

Output shape 계산

  1. 각 Grid cell당 예측해야 하는 값

    • B개의 bounding box 좌표 (x,y,w,h): 4×B
    • B개의 confidence score: 1×B
    • C개의 클래스 확률: C
  2. 전체 계산

    • Cell당 필요한 값 = (4+1)×B + C
    • = 5×3 + 100
    • = 15 + 100
    • = 115
  3. 최종 output shape

    • W = H = S = 12 (grid size)
    • C = 115 (각 cell당 예측값)
    • 따라서 12×12×115 = 139

YOLO v1은 각 grid cell별로 B개의 bounding box(4개의 좌표)와 confidence score, 그리고 C개의 conditional class probabilities를 예측한다. 즉, 각 cell마다 B5+C 개의 예측값이 나오기 때문에 DarkNet의 최종 output shape은 (S, S, (B5+C))이다. 따라서 문제에서 주어진 것처럼 S=12, B=3, C=100일 경우 output shape은 (W, H, C) = (12, 12, (3*5+100)) = (12, 12, 115)이며, W+H+C = 12+12+115 = 139이다





Computing AP

PR Curve를 그리기 위한 표를 보고 실제 AP가 어떻게 계산되는지 직접 계산해 보자.

1x0.2 + 0.8x0.2 + 0.71x0.1 + 0.6x0.1 = 0.49



'Study - AI > Object Detection' 카테고리의 다른 글

Advanced Object Detection  (0) 2025.04.02
EfficientNet & EfficientDet  (0) 2025.02.04
Neck  (0) 2025.01.27
Object Detection Library : MMDetection, Detectron  (0) 2025.01.24
Object Detection Overview  (0) 2025.01.17



백준 BOJ 16961 탭 vs 공백 문제의 파이썬 python 풀이입니다.

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



문제

큐브러버가 운영하는 호텔은 1년 스케줄을 전년도에 미리 정해놓는다. 또, 이 호텔에는 코딩을 할 줄 아는 사람만 투숙할 수 있다. 코딩을 할 때 들여쓰기는 필수이다. 호텔에는 두 종류의 사람이 투숙하는데, 들여쓰기로 탭을 사용하는 사람과 공백을 사용하는 사람이다. 편의를 위해 각각 "탭파"와 "공백파"로 부르도록 하자.

올해는 윤년이기 때문에, 총 366일로 이루어져 있다. 편의상 날짜는 1일-366일로 표시한다.

큐브러버의 호텔에 예약을 한 사람의 수는 N명이고, 각 사람이 투숙 시작일과 종료일을 모두 알고 있다. 모든 사람은 투숙 시작일의 오전 9시에 호텔에 투숙하고, 투숙 종료일의 오후 6시에 호텔에서 나간다. 이 호텔에는 모든 것이 있기 때문에, 호텔 투숙객은 호텔을 벗어나지 않는다.

탭파와 공백파가 만나면 싸운다. 하지만, 탭파와 공백파의 수가 일치하는 날에는 균형이 잡혀서 싸움이 일어나지 않는다. 탭파가 한 명도 없거나 공백파가 한 명도 없으면 호텔 관리자들끼리 왜 호텔 운영을 이런 식으로 하냐며 서로 책임을 돌리면서 싸운다.

호텔 예약 정보가 주어졌을 때, 여러가지 정보를 구해보려고 한다.



입력

첫째 줄에 예약을 한 사람의 수 N(1 ≤ N ≤ 5,000)이 주어진다. 둘째 줄부터 호텔 예약 정보가 한 줄에 하나씩 주어진다.

호텔 예약 정보는 문자 c와 정수 s, e로 이루어져 있다. c가 'T'인 경우는 탭파, 'S'인 경우는 공백파이다. s는 투숙 시작일, e는 투숙 종료일이다. (1 ≤ s ≤ e ≤ 366)



출력

다음 정보를 한 줄에 하나씩 출력한다.

  1. 투숙객이 1명 이상인 날의 수
  2. 가장 많은 투숙객이 있었던 날에 투숙한 사람의 수
  3. 싸움이 없는 날의 수
  4. 싸움이 없는 날 중 가장 많은 투숙객이 있었던 날에 투숙한 사람의 수. 싸움이 없는 날이 없으면 0을 출력한다.
  5. 가장 오랜 기간 투숙한 사람이 투숙한 날의 수




풀이

# 16961 탭 vs 공백

import sys

n = int(sys.stdin.readline().strip()) # 예약한 사람의 수

# 각 날짜별 탭파와 공백파의 수를 기록할 배열 (1일부터 366일까지)
tab = [0] * 367
space = [0] * 367

# 투숙 기간 기록
max_stay = 0

# 모든 예약 정보 처리
for _ in range(n):
    c, s, e = input().split()
    s, e = int(s), int(e)

    # 가장 오랜 기간 투숙한 사람 업데이트
    max_stay = max(max_stay, e - s + 1)

    # 해당 손님의 투숙 날짜 기록
    if c == 'T':  # 탭파
        for day in range(s, e + 1):
            tab[day] += 1

    else:  # 공백파
        for day in range(s, e + 1):
            space[day] += 1

# 결과 계산
one_over = 0    # 투숙객이 1명 이상인 날의 수
max_guests = 0          # 가장 많은 투숙객이 있었던 날의 투숙객 수
no_fight = 0       # 싸움이 없는 날의 수
no_fight_max = 0 # 싸움이 없는 날 중 가장 많은 투숙객이 있었던 날의 투숙객 수

for day in range(1, 367):
    total_guests = tab[day] + space[day]

    # 투숙객이 있는 날 카운트
    if total_guests > 0:
        one_over += 1

    # 최대 투숙객 수 업데이트
    if total_guests > max_guests:
        max_guests = total_guests

    # 싸움이 없는 날 체크 (탭파 == 공백파 && 둘 다 0이 아닌 경우)
    if (tab[day] == space[day]) and (tab[day] > 0):
        no_fight += 1

        # 싸움이 없는 날 중 최대 투숙객 수 업데이트
        if total_guests > no_fight_max:
            no_fight_max = total_guests

# 결과 출력
print(one_over)
print(max_guests)
print(no_fight)
print(no_fight_max)
print(max_stay)
  • 입력 처리
    • 예약한 사람의 수 N을 입력
    • 각 예약 정보(탭파/공백파, 투숙 시작일, 종료일)를 입력
  • 날짜별 투숙객 추적
    • 1일부터 366일까지 각 날짜별로 탭파와 공백파의 수를 기록
    • 각 예약마다 투숙 시작일부터 종료일까지 해당 유형의 손님 수를 증가시킴
  • 통계 계산
    • 투숙객이 1명 이상인 날의 수를 계산
    • 가장 많은 투숙객이 있었던 날의 투숙객 수를 찾음
    • 싸움이 없는 날(탭파와 공백파의 수가 동일하고 둘 다 0이 아닌 경우)의 수를 계산
    • 싸움이 없는 날 중 가장 많은 투숙객이 있었던 날의 투숙객 수를 찾음
    • 가장 오랜 기간 투숙한 사람의 투숙 일수를 계산



'Algorithm > Python' 카테고리의 다른 글

BOJ 23253 자료구조는 정말 최고야  (0) 2025.04.17
LeetCode 46 Permutations  (0) 2025.04.12
LeetCode 17 Letter Combinations of a Phone Number  (0) 2025.04.05
BOJ 27162 Yacht Dice  (0) 2025.03.30
BOJ 25204 문자열 정렬  (0) 2025.03.30



Cascade RCNN

Cascade RCNN은 학습할 bounding box의 IOU threshold를 0.5에서 더 높여보면 어떨까라는 아이디어에서 출발했음. Cascade RCNN은 Iterative BBox at Inference와 Intergral Loss를 결합하여 기존의 Faster RCNN보다 더 좋은 성능을 나타낸다

  • Cascade RCNN은 Iterative BBox at Inference와 Intergral Loss를 사용해서 Bbox pooling을 반복 수행할 시 성능 향상되는 것, IOU threshold가 다른 Classifier가 반복될 때 성능 향상 되는 것을, IOU threshold가 다른 RoI head를 cascade로 쌓을 시 성능 향상되는 것을 증명했다.

Deformable convolution

  • Cascade R-CNN의 주요 특징
    • 기존 컨볼루션의 고정된 수용 영역을 개선
    • 객체의 형태에 따라 동적으로 수용 영역을 조절
    • Iterative BBox와 결합하여 성능 향상
    • 특히 변형이 있는 객체 검출에 효과적

Integral Loss

  • 주로 human pose estimation에서 사용되는 loss 함수임

Transformer

  • DETR 등에서 사용됨

Sliding window

  • Cascade R-CNN의 구성 요소가 아님
  • Region proposal 기반 방식을 사용함

Window Multi-Head Attention

  • Transformer 계열의 기술임
  • Swin Transformer 등에서 사용됨





ViT based detector

ViT를 사용하는 Detection network

  • Swin은 Shifted Window Multi-Head Attention를 통해 receptive field가 제한되는 단점을 해결했다
    • Window 기반 self-attention의 한계를 극복
    • Shifted window 방식으로 window 간 정보 교환 가능
  • Swin은 Shifted Window Multi-Head Attention와 Window Multi-Head Attention를 동시에 사용한다
    • 두 가지 attention 메커니즘을 번갈아가며 사용
    • 연산 효율성과 성능을 모두 확보
  • Swin은 position embedding은 사용하나 class embedding은 사용하지 않는다
    • Position embedding으로 위치 정보 인코딩
    • Class embedding은 사용하지 않는 특징
  • 기타 등등
    • DETR은 end-to-end 학습이 가능
    • NMS와 같은 후처리 과정이 필요없음
    • Set prediction으로 직접 고정된 수의 객체 예측

Transformer & ViT

  • Self attention mechanism의 attention 수식에는 softmax를 포함한다

    • Attention(Q,K,V) = softmax(QK^T/√d)V
    • Softmax로 attention score를 정규화
  • MLP head에는 class embedding vector를 입력하여 최종 결과를 추출한다

    • [CLS] 토큰의 출력을 MLP head에 입력
    • 최종 분류 결과 도출
  • ViT는 learnable embedding앞에 class embedding을 추가 해준다

    • [CLS] 토큰을 patch embedding 시퀀스의 맨 앞에 추가
    • 이 토큰이 전체 이미지의 특징을 종합
  • Transformer는 self attention 메커니즘을 활용 NLP에서 long range dependency를 해결한다

    • Self attention으로 모든 위치 간의 관계 고려
    • RNN의 장거리 의존성 문제 해결
  • ViT는 image를 flatten해 3d로 변형 후 learnable embedding 처리를 한다?
    → 틀린 설명

    • Image는 patch 단위로 분할 후 flatten하여 1D로 변형됨
    • 3D가 아닌 2D embedding으로 변환
    • 순서: Image → Patch분할 → Flatten(1D) → Linear Projection → Embedding





YOLO v4

  • Enhancement of receptive field
    → BOS
    • SPP, ASPP, RFB 등의 기법
    • 연산량이 증가하며 receptive field 확장
    • Inference 시간에 영향을 미침
  • Activation function
    → BOS
    • Mish 등의 새로운 활성화 함수
    • 추가 연산이 필요함
    • Inference 속도에 영향을 미침
  • Data augmentation
    → BOF
    • Cutmix, Mosaic 등
    • 학습 시에만 사용되고 추론 시에는 사용되지 않음
    • Inference 비용 증가 없이 성능 향상
  • Feature Integration
    → BOS
    • FPN, PAN, BiFPN 등
    • 추가적인 네트워크 구조 필요
    • Inference 시간 증가
  • Post-processing method
    → BOS
    • DIoU-NMS 등
    • 추가 연산 필요
    • Inference 시간에 영향을 미침





M2Det

M2Det 모델의 FFM(Feature Fusion Module) 구조가 다음 그림과 같이 구성되어 있고 각각의 input shape이 (512, 32, 32), (256, 128, 128)일 때, output feature map의 shape인 (C, H, W)

입력값

  1. (512, 32, 32)
  2. (256, 128, 128)

과정

  1. 첫 번째 입력 (512, 32, 32)의 처리
    • Conv: 512→128채널, 3x3, padding=1
      • (512, 32, 32) → (128, 32, 32)
    • Upsample 4x4
      • (128, 32, 32) → (128, 128, 128)
  2. 두 번째 입력 (256, 128, 128)의 처리
    • Conv: 256→64채널, 3x3, padding=1
      • (256, 128, 128) → (64, 128, 128)
  3. Concat 연산
    • 첫 번째 경로: (128, 128, 128)
    • 두 번째 경로: (64, 128, 128)
    • Concat 결과: (192, 128, 128)

따라서 output feature map의 shape는 (192, 128, 128)이 됨





CornerNet

  • Anchor box의 주요 단점:
    • Box의 크기, 비율 등 많은 hyperparameter 필요
    • 이러한 설계 과정이 복잡하고 번거로움
    • 많은 수의 anchor box 생성 필요
    • 대부분이 negative sample로 class imbalance 문제 발생
    • 학습 효율성이 떨어짐



'Study - AI > Object Detection' 카테고리의 다른 글

Object Detection Wrap-Up  (0) 2025.04.05
EfficientNet & EfficientDet  (0) 2025.02.04
Neck  (0) 2025.01.27
Object Detection Library : MMDetection, Detectron  (0) 2025.01.24
Object Detection Overview  (0) 2025.01.17