위처럼 작은 상자들이 저마다 무게와 가치를 가지고 있을 때,
15Kg 등 가방의 무게수납제한 하에서 어떤 박스들을 선택해서 가방에 넣어야 가장 많은 돈이 될까?
라는 형식의 문제를 냅색 문제라고 한다.
이때 같은 상자를 중복해서 여러 번 넣을 수 있으면 unbounded knapsack problem(UKP)라고 하고,
단 한번씩만 넣을 수 있으면 0-1 knapsack problem이라고 한다.
(위 두가지 버전 사이에 K번 넣을 수 있는 버전으로 bounded knapsack problem도 있긴 함)
아래는 관련 문제 리스트
https://www.acmicpc.net/problemset?sort=ac_desc&algo=148
0-1 냅색
일반적으로는 0-1 냅색 버전이 가장 흔하다.
이문제는 NP완전에 해당하는 문제로 브루프포스로 모든 경우의 수를 해보는 것 이외에 더 효율적인 방법은 없는 것으로 알려져 있다.
모든 경우의 수를 해보려면 상자의 개수가 n개일 때 $O(2^n)$의 복잡도를 가진다(0-1 냅색의 경우)
n의 개수가 대략 20 이하일 때는 브루트 포스로 풀수 있다. 이 문제를 통해 체크해보자.
내 브루트포스 답안은 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
#include <bits/stdc++.h>
using namespace std;
#define REP(i,n) for(int i=0;i<(int)(n);i++)
int32_t main()
{
int N; scanf("%d", &N); // 사람수 <=20
int L[21] = {}; REP(i, N)scanf("%d", L + i); // 체력 <= 100
int J[21] = {}; REP(i, N)scanf("%d", J + i); // 기쁨 <= 100
int ans = 0;
for (int i = 0; i < (1 << N); i++) {
int jsum = 0;
int lsum = 0;
for (int j = 0; j < N; j++) {
if (i & (1 << j)) jsum += J[j], lsum += L[j];
}
if (lsum < 100) ans = max(ans, jsum);
}
printf("%d\n", ans);
return 0;
}
|
cs |
n의 개수가 20을 넘어갈 때는 DP로 푸는 게 기본이고, 이 문제가 기본에 해당한다고 볼 수 있다.
아래 영상에서 다루었다.
내 경우, 위 풀이에도 나와있지만, 대체로 iterative DP보다 recursive DP(DFS + memoization)가 훨씬 생각하기 수월한 경우가 많았다.
UKP
상자를 반복해서 사용 가능할 경우 UKP문제가 된다. 이 문제가 기본에 해당한다고 볼 수 있다.
이 문제는 recursive DP로는 시간 초과가 났고, iterative DP 버전도 배열 크기 줄이기 신공을 써야 통과했다.
경우에 따라 UKP문제를 0-1 냅색으로 변환해서 시간 복잡도를 낮출 수 있다. 이 문제를 참조하자.
'Programming > Algorithm' 카테고리의 다른 글
너비 우선 탐색(Breadth-first search, BFS) (0) | 2020.03.19 |
---|---|
백트래킹(back tracking) (0) | 2020.03.09 |
이진탐색/이분탐색 (Binary Search) 알고리즘 (0) | 2019.12.09 |
소수(prime) (0) | 2019.03.30 |
약수 출력하기 (0) | 2019.03.24 |