之前的背包算法是基于递归的暴力算法,其时间复杂度为log(n^m),空间复杂度(m),也就是说暴力算法的时间损耗随着规模的增大程指数增长;经过高人指点,终于领悟了基于递推的背包算法,并对其进行了一些优化,使其时间复杂度为log(n * m * p),空间复杂度为1/2 * (m * n),其时间损耗程线性增长
下面是基于递推背包的源代码:
#include <vector> #include <iostream> #include <cassert> #include <cmath> // 一种选择,将作为set的元素,所以要求支持比较运算 template <typename _W, typename _V> class Choise { public: _W weight; _V value; Choise(_W _weight, _V _value) : weight(_weight) , value(_value) { } }; // 一个item,有多种选择,按所选择的控件对选择排序 template <typename _W, typename _V> class Item : public std::vector< Choise<_W, _V> > { }; template <typename _W, typename _V> class ItemList : public std::vector< Item<_W, _V> > { }; typedef std::vector<size_t> ItemChoiceIndexList; // 一个方案,表示某些item在某个空间的最佳解决方案 template <typename _W, typename _V> class Solution { public: ItemChoiceIndexList itemChoiceIndexList; }; template <typename _W, typename _V> class SolutionList : public std::vector< Solution<_W, _V> > { public: size_t low_bound; SolutionList(size_t lb) : low_bound(lb) { } }; template <typename _W, typename _V> class SolutionChart : public std::vector< SolutionList<_W, _V> > { }; // 从一个item中选出最小的weight template <typename _W, typename _V> _W getMinItemWeight(const Item<_W, _V> & item) { assert(!item.empty()); _W minWeight = item.front().weight; for(size_t i = 1; i < item.size(); i++) { if(item[i].weight < minWeight) { minWeight = item[i].weight; } } return minWeight; } // 从一个item中选出最大的weight template <typename _W, typename _V> _W getMaxItemWeight(const Item<_W, _V> & item) { assert(!item.empty()); _W maxWeight = item.front().weight; for(size_t i = 1; i < item.size(); i++) { if(item[i].weight > maxWeight) { maxWeight = item[i].weight; } } return maxWeight; } // 从一个itemList的total min weight template <typename _W, typename _V> _W getTotalMinWeightFromItems(typename ItemList<_W, _V>::const_iterator begin, typename ItemList<_W, _V>::const_iterator end) { _W totalWeight = 0; ItemList<_W, _V>::const_iterator item_iter = begin; for(; item_iter != end; item_iter++) { totalWeight += getMinItemWeight(*item_iter); } return totalWeight; } // 从一个itemList的total max weight template <typename _W, typename _V> _W getTotalMaxWeightFromItems(typename ItemList<_W, _V>::const_iterator begin, typename ItemList<_W, _V>::const_iterator end) { _W totalWeight = 0; ItemList<_W, _V>::const_iterator item_iter = begin; for(; item_iter != end; item_iter++) { totalWeight += getMaxItemWeight(*item_iter); } return totalWeight; } // 计算solution的总weight template <typename _W, typename _V> static _W getSolutionTotalWeight(const ItemList<_W, _V> & itemList, const ItemChoiceIndexList & itemChoiceIndexList) { assert(itemChoiceIndexList.size() <= itemList.size()); _W totalWeight = 0; for(size_t i = 0; i < itemChoiceIndexList.size(); i++) { totalWeight += itemList[i][itemChoiceIndexList[i]].weight; } return totalWeight; } // 计算solution的总value template <typename _W, typename _V> static _V getSolutionTotalValue(const ItemList<_W, _V> & itemList, const ItemChoiceIndexList & itemChoiceIndexList) { assert(itemChoiceIndexList.size() <= itemList.size()); _V totalValue = 0; for(size_t i = 0; i < itemChoiceIndexList.size(); i++) { totalValue += itemList[i][itemChoiceIndexList[i]].value; } return totalValue; } // 从一个item中选出符合capacity的最佳choice template <typename _W, typename _V> bool getBestItemChoice(const Item<_W, _V> & item, _W capacity, size_t & retChoiceIndex, Solution<_W, _V> & retSolution) { assert(!item.empty()); _W best_weight = item.front().weight; _V best_value = item.front().value; size_t best_choice_i = 0; for(size_t choice_i = 1; choice_i < item.size(); choice_i++) { if(item[choice_i].weight <= capacity) { if(item[choice_i].value > best_value) { best_weight = item[choice_i].weight; best_value = item[choice_i].value; best_choice_i = choice_i; } } } if(best_weight > capacity) { return false; } retChoiceIndex = best_choice_i; return true; } // 从一个item和itemChoiceIndexList中选出符合capacity的最佳choice template <typename _W, typename _V> bool getBestItemChoice(const Item<_W, _V> & item, _W capacity, const ItemList<_W, _V> & itemList, const SolutionList<_W, _V> & solutionList, size_t & retChoiceIndex, Solution<_W, _V> & retSolution) { assert(!item.empty()); assert(!itemList.empty()); assert(!solutionList.empty()); _V best_value = item.front().value; size_t best_choice_i = SIZE_MAX; size_t last_capacity_i; for(size_t choice_i = 0; choice_i < item.size(); choice_i++) { if(item[choice_i].weight > capacity) { continue; } _W remainingCapacity = capacity - item[choice_i].weight; if(remainingCapacity < solutionList.low_bound) { continue; } size_t capacity_i = remainingCapacity - solutionList.low_bound; _V totalValue = getSolutionTotalValue( itemList, (capacity_i < solutionList.size() ? solutionList[capacity_i] : solutionList.back()).itemChoiceIndexList) + item[choice_i].value; if(best_value < totalValue) { best_value = totalValue; best_choice_i = choice_i; last_capacity_i = capacity_i < solutionList.size() ? capacity_i : (solutionList.size() - 1); } } assert(best_choice_i != SIZE_MAX); retChoiceIndex = best_choice_i; retSolution = solutionList[last_capacity_i]; return true; } template <typename _T> _T min(const _T & lhs, const _T & rhs) { return lhs < rhs ? lhs : rhs; } // 使用递推背包算法计算最佳choice list template <typename _W, typename _V> bool CalculateBestItemChoiceList(const ItemList<_W, _V> & itemList, _W capacity, ItemChoiceIndexList & retItemChoiceIndexList) { assert(!itemList.empty()); SolutionChart<_W, _V> solutionChart; solutionChart.push_back(SolutionList<_W, _V>(getMinItemWeight(itemList.front()))); SolutionList<_W, _V> & solutionList = solutionChart.back(); if(capacity < solutionList.low_bound) { return false; } solutionList.resize( min(capacity, getMaxItemWeight(itemList.front())) - solutionList.low_bound + 1); // 初始化第一行 for(size_t capacity_i = 0; capacity_i < solutionList.size(); capacity_i++) { Solution<_W, _V> & solution = solutionList[capacity_i]; assert(solution.itemChoiceIndexList.empty()); _W best_choice_i; if(!getBestItemChoice( itemList.front(), solutionList.low_bound + capacity_i, best_choice_i, solution)) { return false; } solution.itemChoiceIndexList.push_back(best_choice_i); } // 逐渐追加item ItemList<_W, _V>::const_iterator item_iter = itemList.begin() + 1; for(; item_iter != itemList.end(); item_iter++) { solutionChart.push_back(SolutionList<_W, _V>(solutionChart.back().low_bound + getMinItemWeight(*item_iter))); SolutionList<_W, _V> & lastSolutionList = *(solutionChart.rbegin() + 1); SolutionList<_W, _V> & solutionList = solutionChart.back(); if(capacity < solutionList.low_bound) { return false; } solutionList.resize( min(capacity, getTotalMaxWeightFromItems<_W, _V>(itemList.begin(), item_iter + 1)) - solutionList.low_bound + 1); // 设置每一个cell for(size_t capacity_i = 0; capacity_i < solutionList.size(); capacity_i++) { Solution<_W, _V> & solution = solutionList[capacity_i]; assert(solution.itemChoiceIndexList.empty()); size_t best_choice_i; if(!getBestItemChoice( *item_iter, solutionList.low_bound + capacity_i, itemList, lastSolutionList, best_choice_i, solution)) { return false; } solution.itemChoiceIndexList.push_back(best_choice_i); } } retItemChoiceIndexList = solutionChart.back().back().itemChoiceIndexList; return true; } int main(int argc, char ** argv) { // 定义weight、value类型 typedef size_t weight_type; typedef double value_type; // 初始化itemList const size_t item_count = 32; const size_t choice_count = 3; ItemList<weight_type, value_type> itemList; for(int i = 0; i < item_count; i++) { Item<weight_type, value_type> item; for(int w = 1; w < 2 * choice_count; w += 2) { item.push_back(Choise<weight_type, value_type>(w, sqrt((value_type)w))); } itemList.push_back(item); } // 对指定totalSpace,求itemList最佳choice组合 const weight_type totalSpace = 100; ItemChoiceIndexList itemChoiceIndexList; if(CalculateBestItemChoiceList<weight_type, value_type>(itemList, totalSpace, itemChoiceIndexList)) { weight_type remainingCapacity = totalSpace - getSolutionTotalWeight(itemList, itemChoiceIndexList); std::cout << "total space: " << totalSpace << ", remaining space:" << remainingCapacity << std::endl; for(size_t i = 0; i < itemList.size(); i++) { std::cout << "item[" << i << "]: "; Item<weight_type, value_type>::const_iterator choice_iter = itemList[i].begin(); for(; choice_iter != itemList[i].end(); choice_iter++) { std::cout << "(" << choice_iter->weight << ", " << choice_iter->value << ") "; } std::cout << " choice:" << itemChoiceIndexList[i] + 1 << std::endl; } } else { std::cout << "cannot calculate choice list for total space: " << totalSpace << std::endl; } return 0; }
其中value值的计算为value = sqrt(weight),这是因为处于平均的原则,希望value(n), value(n)的组合要优越于value(n-1), value(n+1)的组合,这就要求value(n) - value(n-1) > value(n+1) - value(n),也就是n和n+1之间的增量随着n的递加而递减,正好是一个一元二次方程y=x2 的反函数f-1 (y)=sqrt(x)
以下是程序运行输出结果:(可以看到,输出结果分布均匀)
total space: 100, remaining space:0 item[0]: (1, 1) (3, 1.73205) (5, 2.23607) choice:3 item[1]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[2]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[3]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[4]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[5]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[6]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[7]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[8]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[9]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[10]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[11]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[12]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[13]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[14]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[15]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[16]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[17]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[18]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[19]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[20]: (1, 1) (3, 1.73205) (5, 2.23607) choice:3 item[21]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[22]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[23]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[24]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[25]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[26]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[27]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[28]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[29]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[30]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 item[31]: (1, 1) (3, 1.73205) (5, 2.23607) choice:2 続行するには何かキーを押してください . . .