여러 물건들의 가치(v)와 무게(w)가 주어지고, 가방의 최대 사이즈(maximum size)가 주어질 때 가치들의 합이 가장 높도록 물건을 가방에 담는 알고리즘이다.
먼저 분할가능 배낭문제는 간단한 그리디 알고리즘이다.
단위 무게당 가치 순으로 높은 것들부터 담는 욕심쟁이 기법을 쓰면 된다.
예를 들어,

위 표와 같이 7개의 물건들이 주어졌다고 가정해보자. "가격/무게"를 통해 무게 대비 가격이 높은 순으로 정렬하면,
O1 > O5 > O6 > O2 > O3 > O7 > O4 순으로 정렬할 수 있다.(내림차순)
1) O1 선택 -> 가치 합: 10, 남은 무게: 7
2) O5 선택 -> 가치 합: 22, 남은 무게: 5
3) O6 선택 -> 가치 합: 33, 남은 무게: 2
4) O2 선택 -> 가치 합: 40, 남은 무게: 0
주의할 점은 만약 마지막 물건 선택 시 해당 물건 하나를 온전히 담지 못할 수 있다. 이 때 이 물건을 잘라서 넣을 수 있다는 것이 분할가능 배낭 문제와 0/1 배낭문제의 가장 큰 차이점이다.
분할가능 배낭문제에선 (마지막 물건의)단위 무게당 가치 x 남은 무게로 가치가 높도록 물건들을 꽉꽉 담을 수 있다. 따라서 그리디가 적용 가능하다.
반면, 0/1 배낭문제는 그러하지 못하다(아래 설명에 반례 존재)
// 단위무게당 가치로 정렬하는것은 그리디 방식을 적용할 수 있는 분할 가능 배낭 문제가 가능하다.
float solution1(int n, int k, vector<pair<int, int>> input){
// 단위 무게당 가치를 포함하는 자료구조 생성
// <단위 무게당 가치, <무게, 가치>>
vector<pair<float, pair<int, int>>> vec;
for(int i=0; i<n; i++){
int w, v;
w = input[i].first; // 무게
v = input[i].second; // 가치
// 단위당 가치
float perValue = v/(float)w;
vec.push_back({perValue, {w, v}});
}
// 단위 무게당 가치로 정렬
sort(vec.begin(), vec.end(), compare);
int totalWeigh = 0;
float totalValue = 0;
for(int i=0; i<n; i++){
pair<float, pair<int, int>> nowP = vec[i];
int nowWeight = nowP.second.first;
int nowValue = nowP.second.second;
int resWeight = k - totalWeigh;
totalWeigh += nowWeight;
if(totalWeigh > k){
// 이 if문에 걸리는 것은 마지막으로 뽑을 수 있는 물건이라는 뜻이다.
totalValue += vec[i].first * resWeight;
break;
}
totalValue += nowValue;
}
return totalValue;
}
앞서 설명하였든 0/1 배낭문제는 그 물건을 포함하거나 안하거나 둘 중 하나의 선택만을 하여야 한다.
예를 들어 다음과 같은 상황에 있다.

물건의 종류는 4개이고, 가방의 최대 사이즈는 7이다.
물건 1) w: 6, v: 13
물건 2) w: 4, v: 8
물건 3) w: 3, v: 6
물건 4) w: 5, v: 12
만약 분할가능 배낭문제였다면, 물건 4를 1개 선택하고, 물건 1의 1/3개를 선택하면 최대가 된다.
하지만 0/1 배낭문제는 그렇게 해서는 안된다. 0/1 배낭 문제를 단순히 무게 당 가치가 높은 것 부터 선택하면 어떻게 될까? 물건 4만을 뽑을 수 밖에 없으므로 가치합은 12가 될 것이다. 하지만 답은 물건 2와 3을 뽑은 14가 정답이다.
DP를 통해 풀 수 있는 문제이다. 먼저 탑다운 방식이다. 그냥 DFS(조합을 위한)로는 시간초과가 뜬다. 따라서 DFS + 메모제이션을 활용한다. DP테이블은 DP[idx(특정 물건을 가리킴)][현재까지 가치합]이다.
// => 0/1 배낭문제는 다이내믹 프로그래밍
int dfs(int n, int k, vector<pair<int, int>> &vec, int idx, int weight){
// idx번째 물건 체크
// 가치 합이 weight인 경우 메모한 것이 존재하는가?
if(dp[idx][weight] != 0)
return dp[idx][weight];
// 종료 조건
if(weight == k)
return 0;
if(idx == vec.size())
return 0;
// 탐색
int maxValue = 0;
for(int i=idx; i < n; i++){
int nowWeight = vec[i].first;
int nowValue = vec[i].second;
// 백트레킹 추가(탐색을 줄인다.)
if(weight + nowWeight > k)
continue;
maxValue = max(maxValue, nowValue + dfs(n, k, vec, i+1, weight+nowWeight));
}
dp[idx][weight] = maxValue;
return maxValue;
}
int solution2(int n, int k, vector<pair<int, int>> &input){
int answer = 0;
answer = dfs(n, k, input, 0, 0);
return answer;
}

// (3) 바텀업 방식
int solution3(int n, int k, vector<pair<int, int>> &input){
// DP 테이블 이용
vector<int> dpTable(k+1, 0); // 무게별로 메모하는 dp
int answer = 0;
for(int i=0; i<n; i++){ // O(n)
int nowWeight = input[i].first;
int nowValue = input[i].second;
// 뒤에서부터 탐색!
for(int j=k-nowWeight; j>=0; j--){ // => O(n) * O(k) // n <= 100, k <= 100,000
int w = nowWeight + j;
//if(w > k)
// continue;
dpTable[w] = max(dpTable[w], dpTable[j] + nowValue);
answer = max(answer, dpTable[w]);
}
}
return answer;
}