..

배낭 문제(Knapsack Problem)

배낭 문제

  • 제한된 무게의 배낭(knapsack)이 있고 각각 무게와 가치가 주어진 아이템 중
    가치의 합이 최대가 되도록 선택하는 조합 최적화 문제

문제 정의

  • 배낭의 최대 무게: W
  • 각 아이템의 무게: weights = [w1, w2, …, wn]
  • 각 아이템의 가치: values = [v1, v2, …, vn]
  • 목표: 총 무게 <= W 조건하에 가치의 합을 최대화

예시

  • 도둑의 배낭은 최대 15kg까지 담을 수 있다

    상자 무게(kg) 가치($)
    A 12 4
    B 1 2
    C 4 10
    D 1 1
    E 2 2

 

분할 가능한 배낭 문제

  • 아이템을 부분적으로 쪼개서 담을 수 있는 경우

최적 해

  • C(4kg) + B(1kg) + E(2kg) + D(1kg) + A(7kg 일부만)
  • 총 무게 = 15kg
  • 총 가치 = 최대(약 17.33)

코드 리뷰

그리디 알고리즘(Greedy)으로 해결

# (무게, 가치)
items = [(12, 4), (1, 2), (4, 10), (1, 1), (2, 2)]
capacity = 15

# 가치/무게 비율로 정렬 (내림차순)
items.sort(key=lambda x: x[1]/x[0], reverse=True)

total_value = 0
for weight, value in items:
    if capacity >= weight:
        total_value += value
        capacity -= weight
    else:
        # 남은 무게만큼 비율로 담기
        total_value += value * (capacity / weight)
        break

print(f"Fractional Knapsack 총 가치: {total_value}")

시간 복잡도

  • n - 아이템의 수

  • Fractional Knapsack: O(n log n)

0-1 배낭 문제

  • 아이템을 쪼갤 수 없고 전체를 통째로 넣거나 아예 안 넣거나 해야 하는 경우

최적 해

  • C(4kg) + B(1kg) + E(2kg) + D(1kg)
  • A(12kg)는 너무 무거워서 배낭에 안 들어감
  • 총 무게 = 8kg
  • 총 가치 = 최대(15)

동적 계획법(DP)로 해결

코드 리뷰

# (무게, 가치)
items = [(12, 4), (1, 2), (4, 10), (1, 1), (2, 2)]
capacity = 15
n = len(items)

# DP 테이블 생성
dp = [[0] * (capacity + 1) for _ in range(n + 1)]

# DP 채우기
for i in range(1, n + 1):
    w, v = items[i - 1]
    for c in range(capacity + 1):
        if c < w:
            dp[i][c] = dp[i - 1][c]
        else:
            dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - w] + v)

print(f"0-1 Knapsack 총 가치: {dp[n][capacity]}")

시간 복잡도

  • n - 아이템의 수

  • W - 배낭의 최대 용량

  • 0-1 Knapsack: O(n × W)