가치의 합이 최대가 되도록 짐을 고르는 방법을 찾는 문제
분할 가능 배낭 문제(Fractional)
0-1 배낭 문제(0-1 Knapsack)
두 가지로 나눌 수 있고, 주로 0-1 배낭 문제가 다루어진다.
0-1 배낭 문제는 DP를 사용하는 기본적인 문제이다.
n개의 짐이 있다고 할 때, 만들 수 있는 부분 집합은 개이다.
시간 복잡도가 이므로 너무 느리다.
그림과 같이 짐과 가방이 있고, 가격이 비싼 짐부터 고르는 상황을 가정해보자.
6kg / $9
와 4kg / $7
을 고르고, 가격은 총 $16이 된다.
하지만, 그림을 유심히 보면 4kg / $6
, 4kg / $7
, 4kg / $4
를 고르면 $17이 되기 때문에 그리디 알고리즘으로는 최적의 답을 구할 수 없다는 것을 알 수 있다.
i-1번째까지의 짐을 가지고 구한 최적의 가치
가 최대i-1번째까지의 짐을 가지고 구한 최적의 가치 + i번째 짐의 가치
와 i-1번째까지의 짐을 가지고 구한 최적의 가치
중 더 큰 값을 선택2kg/$3
를 고려3kg/$4
이렇게 반복하면서 표를 채우면, 마지막 칸이 결국 모든 짐들을 확인했을 때의 무게 w에서 구할 수 있는 최적의 이익이 된다.
0-1 배낭 문제의 대표적인 문제는 백준 12865번 평범한 배낭이다.
분할 가능 문제에서는 4kg/$8
인 짐이 있다면 2kg/$4
, 2kg/$4
로 짐을 분할하여 배낭에 담을 수 있다.
해당 문제는 그리디 알고리즘으로 무게 대비 가치가 높은 물건부터 담는 방법으로 간단하게 풀 수 있다.
그림과 같이 짐과 가방이 있다고 가정해보자. 각 짐의 무게 대비 가치는 아래와 같다.
$0.333/kg
$2/kg
$2.5/kg
$1/kg
$1/kg
이 때, 1kg을 단위로 C > C > C > C > B > D > E > E > A > A > A > A > A > A > A
순서로 담으면 총 $17.333을 담을 수 있다.
cargo = [
(4, 12),
(2, 1),
(10, 4),
(1, 1),
(2, 2)
]
def fractional_knapsack(cargo):
capacity = 15
pack = []
for c in cargo:
pack.append((c[0]/c[1], c[0], c[1]))
pack.sort(reverse = True)
total_value: float = 0
for p in pack:
if capacity - p[2] >= 0:
capacity -= p[2]
total_value += p[1]
else:
fraction = capacity / p[2]
total_value += p[1] * fraction
break
return total_value