NeFut Logo NeFut
Admin Login

Efficient Algorithm Analysis: Optimization and Implementation of the Knapsack Problem

Published at: 2026-05-27 09:17 Last updated: 2026-06-06 13:04
#C++ #Tutorial

Core Logic and Mathematical Principles

The knapsack problem is a classic example of a high-dimensional constrained combinatorial optimization problem. Its essence lies in how to achieve the optimal value of the objective function (total value) through a phased scan of discrete decisions (to select or not, how many to select) under a finite resource boundary (knapsack capacity $V$).

The mathematical essence of the three core knapsack models varies:

  1. 0-1 Knapsack: Each item is available only once. The decision has a dual nature, and the state transition relies on the pure history of the previous stage that is not contaminated by the current item.
  2. Unbounded Knapsack: Each item is available in unlimited quantities. The decision has a continuous nature, and the state transition relies on the latest state that has included the current item.
  3. Bounded Knapsack: Each item has a fixed count limit $C_i$. Direct brute-force splitting can lead to a complexity degradation to $O(V \sum C_i)$.

$$f[r + q \cdot w_i] = \max_{q - C_i \le k \le q} \{ f[r + k \cdot w_i] - k \cdot v_i \} + q \cdot v_i$$

This is a standard sliding window maximum problem. By maintaining a monotonically decreasing sequence of $f[r + k \cdot w_i] - k \cdot v_i$ using a monotonic queue, the state transition time can be optimized to $O(1)$, reducing the global complexity to the limit of $O(N \cdot V)$.


State Design and Algorithm Derivation

After compressing space using a one-dimensional rolling array, the state space, transition equations, and loop sequences of various knapsack types exhibit a rigorous geometric logic:

1. 0-1 Knapsack

2. Unbounded Knapsack

3. Bounded Knapsack Binary Splitting


C++ Standard Source Code (NOIP Style)

The following source code provides an efficient one-dimensional implementation of 0-1 knapsack, unbounded knapsack, and bounded knapsack (binary splitting) within the same program.

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

const int MAXV = 20005; // For knapsack capacity
int f[MAXV];

// Structure for flattened items resulting from splitting in bounded knapsack
struct Item {
    int w, v;
};

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    int n, max_v;
    if (!(cin >> n >> max_v)) return 0;

    vector<Item> items;

    for (int i = 1; i <= n; ++i) {
        int w, v, c, type;
        cin >> w >> v >> c >> type; // type: 1->0-1, 2->unbounded, 3->bounded

        if (type == 1) {
            items.push_back({w, v});
        } 
        else if (type == 2) {
            // Unbounded knapsack will be processed separately in ascending order later; here for unified scheduling, it can be temporarily excluded from the 0-1 queue
            // For high cohesion in competitive programming, it is common to handle all three types of knapsacks in the main loop
        } 
        else if (type == 3) {
            // Bounded knapsack: core logic for binary splitting
            for (int k = 1; c >= k; k <<= 1) {
                items.push_back({k * w, k * v});
                c -= k;
            }
            if (c > 0) {
                items.push_back({c * w, c * v});
            }
        }
    }

    // ==================== Unified descending drive for 0-1 knapsack & bounded knapsack (after splitting) ====================
    for (const auto& item : items) {
        for (int j = max_v; j >= item.w; --j) { // Critical pitfall: must be strictly in descending order, otherwise it degenerates into unbounded knapsack
            f[j] = max(f[j], f[j - item.w] + item.v);
        }
    }

    // ==================== Independent ascending drive for unbounded knapsack (assuming a group of unbounded knapsack items is read in separately) ====================
    // This is only for demonstration: if the item is of unbounded knapsack type, the loop must be rewritten in ascending order
    int comp_w = 5, comp_v = 10; // Simulating an unbounded knapsack item
    for (int j = comp_w; j <= max_v; ++j) { // Ascending drive, allowing state self-contamination, accommodating multiple items
        f[j] = max(f[j], f[j - comp_w] + comp_v);
    }

    cout << f[max_v] << "\n";

    return 0;
}

NOIP Practical Pitfall Guide

1. Incorrect Loop Direction for Capacity in 0-1 Knapsack One-Dimensional Compression

2. Missing Remaining Tail in Bounded Knapsack Binary Splitting


Classic NOIP/Luogu Problems

1. Luogu P1776 Treasure Selection

2. Luogu P1616 Crazy Herb Picking

Original Source: local://14.3

[h] Back to Home