在工作中遇到一个奇怪的要求,给定空间和时间,要求将特定设备的配置最佳化,这就用到了“背包”算法,很有趣,我写了一个比较通用的算法(原理就是穷举),下面是源代码:
#include <vector>
#include <cassert>
#include <cmath>
#include <iostream>
template <typename _W, typename _V>
class Choice
{
public:
_W weight;
_V value;
Choice(_W _weight, _V _value)
: weight(_weight)
, value(_value)
{
}
};
template <typename _W, typename _V>
class Item
: public std::vector< Choice<_W, _V> >
{
};
template <typename _W, typename _V>
class ItemList
: public std::vector< Item<_W, _V> >
{
};
class ItemChoiceIndexList
: public std::vector<size_t>
{
};
template <typename _W, typename _V>
bool CalculateBestItemChoiceList(_W capacity,
typename ItemList<_W, _V>::const_iterator item_begin,
typename ItemList<_W, _V>::const_iterator item_end,
ItemChoiceIndexList & retItemChoiceList,
_W & retTotalWeight,
_V & retTotalValue)
{
assert(retItemChoiceList.empty());
// 深度检测
if(item_begin == item_end)
{
retTotalWeight = 0;
retTotalValue = 0;
return true;
}
bool findMinChoiceList = false;
_V maxTotalValue = 0;
for(size_t choice_i = 0; choice_i < item_begin->size(); choice_i++)
{
const Choice<_W, _V> & choice = (*item_begin)[choice_i];
assert(choice.value > 0);
// 如果当前选项大于总空间,本次组合不可选
if(choice.weight > capacity)
{
continue;
}
// 获取剩下总空间的最佳背包组合
ItemChoiceIndexList retRemainingItemChoiceList;
_W retRemainingTotalWeight;
_V retRemainingTotalValue;
if(!CalculateBestItemChoiceList(
capacity - choice.weight,
item_begin + 1,
item_end,
retRemainingItemChoiceList,
retRemainingTotalWeight,
retRemainingTotalValue))
{
continue;
}
// 如果这次的组合获得的剩余空间最小,这保存这次的组合
if(retRemainingTotalValue + choice.value > maxTotalValue)
{
findMinChoiceList = true;
maxTotalValue = retRemainingTotalValue + choice.value;
retItemChoiceList.clear();
retItemChoiceList.push_back(choice_i);
retItemChoiceList.insert(
retItemChoiceList.end(), retRemainingItemChoiceList.begin(), retRemainingItemChoiceList.end());
retTotalValue = maxTotalValue;
retTotalWeight = retRemainingTotalWeight + choice.weight;
}
}
// 没有找到合适的组合
if(!findMinChoiceList)
{
return false;
}
assert(!retItemChoiceList.empty());
return true;
}
int main(int argc, char ** argv)
{
typedef unsigned int WeightType;
typedef float ValueType;
// 初始化ItemList用以测试
ItemList<WeightType, ValueType> itemList;
const int item_count = 9;
const int choice_count = 3;
for(int i = 0; i < item_count; i++)
{
Item<WeightType, ValueType> item;
for(int w = 1; w < 2 * choice_count; w += 2)
{
item.push_back(Choice<WeightType, ValueType>(w, sqrt((float)w)));
}
itemList.push_back(item);
}
// 设置总空间
const WeightType capacity = 35;
// 测试,并获得最小剩余空间
ItemChoiceIndexList itemChoiceList;
WeightType totalWeight;
ValueType totalValue;
if(!CalculateBestItemChoiceList(
capacity,
itemList.begin(),
itemList.end(),
itemChoiceList,
totalWeight,
totalValue))
{
std::cout << "cannot calculate best item choice list" << std::endl;
}
else
{
std::cout << "the best choice list: total = "
<< capacity << ", remaining = " << capacity - totalWeight << std::endl;
for(size_t i = 0; i < itemList.size(); i++)
{
std::cout << "item [" << i << "]:";
Item<WeightType, ValueType>::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: " << itemChoiceList[i] + 1 << std::endl;
}
}
return 0;
}