RosettaCodeData/Task/Knapsack-problem-Bounded/C++/knapsack-problem-bounded.cpp

237 lines
7.4 KiB
C++

#include <iostream>
#include <vector>
#include <algorithm>
#include <stdexcept>
#include <memory>
#include <sys/time.h>
using std::cout;
using std::endl;
class StopTimer
{
public:
StopTimer(): begin_(getUsec()) {}
unsigned long long getTime() const { return getUsec() - begin_; }
private:
static unsigned long long getUsec()
{//...you might want to use something else under Windows
timeval tv;
const int res = ::gettimeofday(&tv, 0);
if(res)
return 0;
return tv.tv_usec + 1000000 * tv.tv_sec;
}
unsigned long long begin_;
};
struct KnapsackTask
{
struct Item
{
std::string name;
unsigned w, v, qty;
Item(): w(), v(), qty() {}
Item(const std::string& iname, unsigned iw, unsigned iv, unsigned iqty):
name(iname), w(iw), v(iv), qty(iqty)
{}
};
typedef std::vector<Item> Items;
struct Solution
{
unsigned v, w;
unsigned long long iterations, usec;
std::vector<unsigned> n;
Solution(): v(), w(), iterations(), usec() {}
};
//...
KnapsackTask(): maxWeight_(), totalWeight_() {}
void add(const Item& item)
{
const unsigned totalItemWeight = item.w * item.qty;
if(const bool invalidItem = !totalItemWeight)
throw std::logic_error("Invalid item: " + item.name);
totalWeight_ += totalItemWeight;
items_.push_back(item);
}
const Items& getItems() const { return items_; }
void setMaxWeight(unsigned maxWeight) { maxWeight_ = maxWeight; }
unsigned getMaxWeight() const { return std::min(totalWeight_, maxWeight_); }
private:
unsigned maxWeight_, totalWeight_;
Items items_;
};
class BoundedKnapsackRecursiveSolver
{
public:
typedef KnapsackTask Task;
typedef Task::Item Item;
typedef Task::Items Items;
typedef Task::Solution Solution;
void solve(const Task& task)
{
Impl(task, solution_).solve();
}
const Solution& getSolution() const { return solution_; }
private:
class Impl
{
struct Candidate
{
unsigned v, n;
bool visited;
Candidate(): v(), n(), visited(false) {}
};
typedef std::vector<Candidate> Cache;
public:
Impl(const Task& task, Solution& solution):
items_(task.getItems()),
maxWeight_(task.getMaxWeight()),
maxColumnIndex_(task.getItems().size() - 1),
solution_(solution),
cache_(task.getMaxWeight() * task.getItems().size()),
iterations_(0)
{}
void solve()
{
if(const bool nothingToSolve = !maxWeight_ || items_.empty())
return;
StopTimer timer;
Candidate candidate;
solve(candidate, maxWeight_, items_.size() - 1);
convertToSolution(candidate);
solution_.usec = timer.getTime();
}
private:
void solve(Candidate& current, unsigned reminderWeight, const unsigned itemIndex)
{
++iterations_;
const Item& item(items_[itemIndex]);
if(const bool firstColumn = !itemIndex)
{
const unsigned maxQty = std::min(item.qty, reminderWeight/item.w);
current.v = item.v * maxQty;
current.n = maxQty;
current.visited = true;
}
else
{
const unsigned nextItemIndex = itemIndex - 1;
{
Candidate& nextItem = cachedItem(reminderWeight, nextItemIndex);
if(!nextItem.visited)
solve(nextItem, reminderWeight, nextItemIndex);
current.visited = true;
current.v = nextItem.v;
current.n = 0;
}
if(reminderWeight >= item.w)
{
for (unsigned numberOfItems = 1; numberOfItems <= item.qty; ++numberOfItems)
{
reminderWeight -= item.w;
Candidate& nextItem = cachedItem(reminderWeight, nextItemIndex);
if(!nextItem.visited)
solve(nextItem, reminderWeight, nextItemIndex);
const unsigned checkValue = nextItem.v + numberOfItems * item.v;
if ( checkValue > current.v)
{
current.v = checkValue;
current.n = numberOfItems;
}
if(!(reminderWeight >= item.w))
break;
}
}
}
}
void convertToSolution(const Candidate& candidate)
{
solution_.iterations = iterations_;
solution_.v = candidate.v;
solution_.n.resize(items_.size());
const Candidate* iter = &candidate;
unsigned weight = maxWeight_, itemIndex = items_.size() - 1;
while(true)
{
const unsigned currentWeight = iter->n * items_[itemIndex].w;
solution_.n[itemIndex] = iter->n;
weight -= currentWeight;
if(!itemIndex--)
break;
iter = &cachedItem(weight, itemIndex);
}
solution_.w = maxWeight_ - weight;
}
Candidate& cachedItem(unsigned weight, unsigned itemIndex)
{
return cache_[weight * maxColumnIndex_ + itemIndex];
}
const Items& items_;
const unsigned maxWeight_;
const unsigned maxColumnIndex_;
Solution& solution_;
Cache cache_;
unsigned long long iterations_;
};
Solution solution_;
};
void populateDataset(KnapsackTask& task)
{
typedef KnapsackTask::Item Item;
task.setMaxWeight( 400 );
task.add(Item("map",9,150,1));
task.add(Item("compass",13,35,1));
task.add(Item("water",153,200,2));
task.add(Item("sandwich",50,60,2));
task.add(Item("glucose",15,60,2));
task.add(Item("tin",68,45,3));
task.add(Item("banana",27,60,3));
task.add(Item("apple",39,40,3));
task.add(Item("cheese",23,30,1));
task.add(Item("beer",52,10,3));
task.add(Item("suntancream",11,70,1));
task.add(Item("camera",32,30,1));
task.add(Item("T-shirt",24,15,2));
task.add(Item("trousers",48,10,2));
task.add(Item("umbrella",73,40,1));
task.add(Item("w-trousers",42,70,1));
task.add(Item("w-overclothes",43,75,1));
task.add(Item("note-case",22,80,1));
task.add(Item("sunglasses",7,20,1));
task.add(Item("towel",18,12,2));
task.add(Item("socks",4,50,1));
task.add(Item("book",30,10,2));
}
int main()
{
KnapsackTask task;
populateDataset(task);
BoundedKnapsackRecursiveSolver solver;
solver.solve(task);
const KnapsackTask::Solution& solution = solver.getSolution();
cout << "Iterations to solve: " << solution.iterations << endl;
cout << "Time to solve: " << solution.usec << " usec" << endl;
cout << "Solution:" << endl;
for (unsigned i = 0; i < solution.n.size(); ++i)
{
if (const bool itemIsNotInKnapsack = !solution.n[i])
continue;
cout << " " << solution.n[i] << ' ' << task.getItems()[i].name << " ( item weight = " << task.getItems()[i].w << " )" << endl;
}
cout << "Weight: " << solution.w << " Value: " << solution.v << endl;
return 0;
}