[Algorithm] 0-1 배낭문제(Knapsack Problem) - 백트래킹

2025. 2. 7. 21:35· → 알고리즘 관련
목차
  1. 🥸 지난 글
  2. 🛤️ Backtracking 백트래킹?
  3. 🦖 Brute-Force 브루트 포스
  4. 🚶 다시 백트래킹

🥸 지난 글

이전 글에서 0-1 배낭문제를 다이내믹 프로그래밍으로 푸는 방법을 소개했다. 이번에는 백트래킹으로 풀어보려고 한다.

 

[Algorithm] 0-1 배낭문제(Knapsack Problem) - DP 풀이

알고리즘 하면 나오는 대표 문제인 0-1 배낭문제에 대해서 글을 써보려고 한다.🎒 0-1 배낭 문제왜 0-1 배낭 문제일까? 0-1의 의미는 배낭에 넣기, 안 넣기 둘 중 하나만 선택이 되고, 일부만 넣는

swift-library.tistory.com

🛤️ Backtracking 백트래킹?

나는 좀 더 간단하게 설명해보고 싶었다. 뭔가 의외로 엄청 어렵게 설명되어 있는 글들이 많았다... Backtracking이라는 단어의 뜻을 살펴보면 추적을 종료한다는 뜻으로도 보인다. 뭘 종료할 건데?? 이를 위해서 우선 Brute-Force(부르트 포스)부터 알아보는 것이 좋을 것 같다.

🦖 Brute-Force 브루트 포스

단어를 해석해보면 난폭한 힘...이라는 뜻으로 해석할 수 있는데, 딱 잘 들어맞는다. 말 그대로 무식하게 푼다는 뜻이다. 위의 배낭 문제를 가장 단순하게 풀어보는 방법은 그냥 모든 경우의 수를 다 넣어보는 것이다.

func knapsack(_ capacity: Int, _ weights: [Int], _ values: [Int]) -> Int {
    var result = 0
    
    func dfs(depth: Int, weight: Int, value: Int) {        
        if weight <= capacity {
            result = max(result, value)
        }
        
        guard depth < weights.count else { return }
                
        dfs(depth: depth + 1, weight: weight + weights[depth], value: value + values[depth])
        dfs(depth: depth + 1, weight: weight, value: value)
    }
    
    dfs(depth: 0, weight: 0, value: 0)
            
    return result
}

나는 dfs를 활용하여 부르트 포스로 배낭문제를 풀어보았다. 단순하다. ABCDE순차적으로 가면서 넣는 경우 안 넣는 경우 모든 경우의 수를 가지 늘리듯이 늘리는 것이다. 그렇게 쭉 간다음에 담은 무게가 배낭의 용량에 들어가면서 가장 높은 값어치를 찾는 방식으로 구현했다.

 

이렇게 구현한다면 ABCDE, 5개의 물건을 넣거나, 안 넣거나의 경우일 것이고 2^5가 되어 32가지 방법만 계산해보면 정답을 알 수 있다. 하지만 이 방법의 ☠️ 단점은 무엇일까? 물건이 하나씩 늘어날 때 마다 계산하는 경우의 수가 두 배씩 늘어난다는 것이다. 예를 들면 10개의 물건이 있다면 1024번 계산을 해야 답을 알 수 있는 것이다. 이전 글에서 보여줬던 DP로 푸는 방법의 경우는 배낭의 무게가 15kg이라면 150번에 끝난다.

🚶 다시 백트래킹

그렇다면 어떻게 해야 계산을 최대한 줄일 수 있을까? 배낭에 물건을 넣는다고 해보자. 배낭에 물건을 넣다가 15kg이 초과했다. 그러면 배낭에 물건을 더 이상 넣을 수 없거니와 그 뒤에 경우의 수는 생각할 필요도 없다.

func knapsack(_ capacity: Int, _ weights: [Int], _ values: [Int]) -> Int {
    var result = 0
    
    func dfs(depth: Int, weight: Int, value: Int) {
              /// 현재 담은 무게는 용량보다 같거나 작아야 한다.
        guard weight <= capacity else { return }
        
        if weight <= capacity {
            result = max(result, value)
        }
        
        guard depth < weights.count else { return }
                
        dfs(depth: depth + 1, weight: weight + weights[depth], value: value + values[depth])
        dfs(depth: depth + 1, weight: weight, value: value)
    }
    
    dfs(depth: 0, weight: 0, value: 0)
            
    return result
}

여기서 한번 더 나아가서 만약에 계산해야 하는 물건을 다 합쳐도 지금 최댓값을 못넘게 된다면 탐색할 필요도 없어진다.

func knapsack(_ capacity: Int, _ weights: [Int], _ values: [Int]) -> Int {
    var result = 0
    
    func dfs(depth: Int, weight: Int, value: Int) {
              /// 현재 담은 무게는 용량보다 같거나 작아야 한다.
        guard weight <= capacity,
              /// 남은 물건의 값과 현재 값을 합치면 최댓값보다 커야 한다.
              value + values[depth...].reduce(0, +) > result else { return }
        
        if weight <= capacity {
            result = max(result, value)
        }
        
        guard depth < weights.count else { return }
                
        dfs(depth: depth + 1, weight: weight + weights[depth], value: value + values[depth])
        dfs(depth: depth + 1, weight: weight, value: value)
    }
    
    dfs(depth: 0, weight: 0, value: 0)
                
    return result
}

이렇게 하면 기존에 32번 계산해야 하는 것을 32번 → 26번 → 12번으로 확 줄일 수가 있다. 이 방법을 백트래킹이라고 한다. 사실 백트래킹은 모든 경우의 수를 계산하기 전에 탈출 조건을 만들어 준다고 생각하면 될 것 같다.

 

부르트 포스로 구현했더라도 어디서 끊어줘야 최대한 계산을 줄일 수 있을지 고민해 보자 ☺️

'→ 알고리즘 관련' 카테고리의 다른 글

[Algorithm] 플로이드 워셜 알고리즘  (1) 2025.02.08
[Algorithm] 다익스트라 알고리즘  (0) 2025.02.08
[Algorithm] 0-1 배낭문제(Knapsack Problem) - DP  (0) 2025.02.07
[Algorithm] 2차원 배열 회전하기 (Swift)  (0) 2025.01.30
[Algorithm] 세그먼트 트리(Segment Tree) Swift로 구현하기  (0) 2024.07.11
  1. 🥸 지난 글
  2. 🛤️ Backtracking 백트래킹?
  3. 🦖 Brute-Force 브루트 포스
  4. 🚶 다시 백트래킹
'→ 알고리즘 관련' 카테고리의 다른 글
  • [Algorithm] 플로이드 워셜 알고리즘
  • [Algorithm] 다익스트라 알고리즘
  • [Algorithm] 0-1 배낭문제(Knapsack Problem) - DP
  • [Algorithm] 2차원 배열 회전하기 (Swift)
Swift librarian
Swift librarian
Swift librarian
Swift Library
Swift librarian
전체
오늘
어제
  • 분류 전체보기 (231)
    • 📺 Programming (5)
    • → Architecture (2)
    • → Design Pattern (0)
    • → Computer Science (15)
    • ⚙️ Algorithm (0)
    • → 알고리즘 관련 (22)
    • → Problems (104)
    • 🚀 Project (0)
    • → 알쏭달쏭 (0)
    • → Shook (2)
    • → Solver (8)
    • → Taster (7)
    • → Outline (4)
    • → Pointer (2)
    • → Guesser (3)
    • 🦜 Swift (2)
    • → Swift Archive (12)
    • → Swift Study (12)
    • → Xcode (6)
    • 🧰 Framework (0)
    • → Foundation (1)
    • → UIKit (2)
    • → SwiftUI (3)
    • → CoreData (2)
    • → MapKit (1)
    • → CoreHaptic (1)
    • → User Notification (1)
    • → StoreKit (2)
    • 🏛️ Library (0)
    • → TCA (0)
    • 🐈‍⬛ Git (8)
    • → Git의 원리 (2)
    • → Git 심화 (1)
    • 📦 Other (1)
    • 👦🏻 Log (0)

최근 글

hELLO · Designed By 정상우.v4.2.2
Swift librarian
[Algorithm] 0-1 배낭문제(Knapsack Problem) - 백트래킹
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.