Algoteka
Log in to submit your own samples!

Knapsack Problem

Problem by oml1111
# Tech tags Title Creator Created date
1 0
2022-11-06 00:17
2 0
2022-11-08 02:55
View all samples for this language (2 verified and 0 unverified)

O(n 2^{n/2}) Meet-in-the-middle solution | C++ |

By oml1111 |
0 likes

A readable and commented \(O(n 2^{\frac{n}{2}})\) meet-in-the-middle solution for the knapsack problem (\(n\) is the number of items). Partitions the items into two groups of (roughly) equal size, finds all possible arrangements for both groups individually and then uses the found and sorted arrangements for subgroups to find an optimal solution to the full set of items. Has \(O( 2^{\frac{n}{2}})\) memory complexity.

Code

#include<vector>
#include<algorithm>


struct KnapsackSolver {
    struct Item {
        int weight;
        int value;
    };
    std::vector<Item> items_;
    
    void add_item(int weight, int value) {
        items_.push_back({weight, value});
    }
    
    // Exhaustive search over all the subsets of `item_set`.
    // Output resulting (total weight, total value) pairs to `results`
    void _get_subset_combinations(std::vector<Item> &item_set,
                                  std::vector<std::pair<int, int> > &results,
                                  int i=0,
                                  int total_weight = 0,
                                  int total_value = 0) {
        if(i == (int)item_set.size()) {
            results.push_back({total_weight, total_value});
            return;
        }
        _get_subset_combinations(item_set,
                                 results,
                                 i+1,
                                 total_weight,
                                 total_value);  // Don't take the item
                                 
        _get_subset_combinations(item_set,
                                 results,
                                 i+1,
                                 total_weight + item_set[i].weight,
                                 total_value + item_set[i].value);  // Take the item
    }
    
    // Clean a computed results array so we have increasing (weight, value) pairs
    void _clean_results(std::vector<std::pair<int, int> > &results) {
        std::sort(results.begin(), results.end());
        
        int size=0;  // results[size] tracks the last candidate of addition
        for(int j=0; j < (int)results.size(); j++) {
            if(results[size].first < results[j].first && results[size].second < results[j].second) {
                size++;
                std::swap(results[size], results[j]);  // Greater weight and value, so append to the end
            }
            else if (results[size].second < results[j].second) {
                std::swap(results[size], results[j]);  // Greater value at same weight, so swap with the last candidate
            }
        }
        size++;  // Last candidate is valid by default
        results.resize(size);
    }
    
    int solve_knapsack_problem(int capacity) {
        // First we divide items into two parts of rougly equal size
        int left_size = items_.size()/2;
        std::vector<Item> left_items(items_.begin(), items_.begin() + left_size);
        std::vector<Item> right_items(items_.begin() + left_size, items_.end());
        
        // Next we find all arrangements for both halves and clean them
        std::vector<std::pair<int, int> > left_results, right_results;
        _get_subset_combinations(left_items, left_results);
        _get_subset_combinations(right_items, right_results);
        
        _clean_results(left_results);
        _clean_results(right_results);
        
        // Finally go through left-side results and right-side results in opposite order in order to find the optimal
        // solution to the full item set
        int result_value = 0;
        for(int i=0, j=right_results.size()-1; i < (int)left_results.size(); i++) {
            while(j >= 0 && left_results[i].first + right_results[j].first > capacity) {
                j--;  // Decrement j until the weights of left_result i and right_result j fit within knapsack
            }
            if(j >= 0) {
                // Got a valid combination, now update the result value
                result_value = std::max(result_value, left_results[i].second + right_results[j].second);
            }
        }
        
        return result_value;
    }
};
 
int main() {
    KnapsackSolver solver;
    solver.add_item(5, 7);
    solver.add_item(4, 4);
    solver.add_item(2, 4);
    int result = solver.solve_knapsack_problem(6);
}

Further reading

Knapsack problem: Meet-in-the-middle - wikipedia.org

References

classes
std::pair en.cppreference.com cplusplus.com
std::vector en.cppreference.com cplusplus.com
functions
std::max en.cppreference.com cplusplus.com
std::pair::pair en.cppreference.com cplusplus.com
std::sort en.cppreference.com cplusplus.com
std::swap en.cppreference.com cplusplus.com
std::vector::operator[] en.cppreference.com cplusplus.com
std::vector::push_back en.cppreference.com cplusplus.com
std::vector::resize en.cppreference.com cplusplus.com
std::vector::size en.cppreference.com cplusplus.com

Problem Description

Implement a solution for the Knapsack problem, that is, given a set of items with weights and values, find the maximum total value you can put into a knapsack of some limited capacity. Your solution should optimize for algorithmic complexity or speed. Create some instance of this problem with some set of items and output the maximum attainable total value for some capacity.

Knapsack problem - wikipedia.org

View sample discussion (0 comments)
View problem discussion (0 comments)