1. 问题描述
设有 n 件物品,其中第 i 件的重量和价值分别为 w[ i ] 和 v[ i ], i = 1, ..., n。现在有一个容量为 V 的背包,要求选择其中的若干件装入背包,使得物品的总重量不超过背包的容量,并且总的价值最大。由于每件物品要么被选要么被不选,因此称为 0-1 背包问题。设 w[ i ],v[ i ],V 均为整数。
2. 动态规划解
用 opt[ i, j ] 表示用容量为 j 的背包装前 i 件物品的最大价值。则
- opt[ i, 0 ] = 0, i = 0, 1, ..., n //没有容量了
- opt[ 0, j ] = 0 //没有东西可装
- 对于i > 0, j > 0
- 如果 j >= w[ i ], 则 opt[ i, j ] = max{ v[ i ] + opt[ i - 1, j - w[ i ] ], opt[ i - 1, j ]
- 如果 j < w[ i ], 则 opt[ i, j ] = opt[ i - 1, j ]
#include<iostream> #include<vector> #include<iterator> #include<tuple> template<class Iter1, class Iter2> std::pair<size_t, std::vector<size_t>> knapsack(Iter1 beg_w, Iter1 end_w, Iter2 beg_v, size_t V) { const int n = end_w - beg_w; std::vector<std::vector<size_t>> opt(n + 1, std::vector<size_t>(V + 1)); for(int i = 0; i <= n; ++i) opt[i][0] = 0; for(int j = 0; j <= V; ++j) opt[0][j] = 0; for(int i = 1; i <= n; ++i, ++beg_w, ++beg_v) for(int j = 1; j <= V; ++j) { if(*beg_w <= j) { auto tmp = opt[i - 1][j - *beg_w] + *beg_v; opt[i][j] = (tmp > opt[i - 1][j]) ? tmp : opt[i - 1][j]; } else opt[i][j] = opt[i - 1][j]; } //traceback std::vector<size_t> ans(n, 0); --beg_w; auto volume = V; for(int i = n; i > 0; --i) { if(opt[i][volume] > opt[i - 1][volume]) { ans[i - 1] = 1; volume -= *beg_w; } --beg_w; } return std::make_pair(opt.back().back(), ans); } int main() { std::vector<size_t> w = {5, 8, 5, 10}; std::vector<size_t> v = {10, 16, 7, 10}; std::vector<size_t> ans; size_t opt; std::tie(opt, ans) = knapsack(w.begin(), w.end(), v.begin(), 10); std::cout << opt << std::endl; for(auto x : ans) std::cout << x << ","; std::cout << std::endl; }