25#include "absl/log/check.h"
26#include "absl/log/log.h"
27#include "absl/strings/string_view.h"
28#include "absl/time/time.h"
40const int kNoSelection = -1;
41const int kPrimaryPropagatorId = 0;
42const int kMaxNumberOfBruteForceItems = 30;
43const int kMaxNumberOf64Items = 64;
47struct CompareKnapsackItemsInDecreasingEfficiencyOrder {
48 explicit CompareKnapsackItemsInDecreasingEfficiencyOrder(int64_t _profit_max)
49 : profit_max(_profit_max) {}
52 return item1->GetEfficiency(profit_max) > item2->GetEfficiency(profit_max);
54 const int64_t profit_max;
62struct CompareKnapsackSearchNodePtrInDecreasingUpperBoundOrder {
63 bool operator()(
const KnapsackSearchNode* node_1,
64 const KnapsackSearchNode* node_2)
const {
65 const int64_t profit_upper_bound_1 = node_1->profit_upper_bound();
66 const int64_t profit_upper_bound_2 = node_2->profit_upper_bound();
67 if (profit_upper_bound_1 == profit_upper_bound_2) {
68 return node_1->current_profit() < node_2->current_profit();
70 return profit_upper_bound_1 < profit_upper_bound_2;
74typedef std::priority_queue<
76 CompareKnapsackSearchNodePtrInDecreasingUpperBoundOrder>
80inline bool WillProductOverflow(int64_t value_1, int64_t value_2) {
85 const int kOverflow = 61;
86 return MostSignificantBitPosition1 + MostSignificantBitPosition2 > kOverflow;
90int64_t UpperBoundOfRatio(int64_t numerator_1, int64_t numerator_2,
91 int64_t denominator) {
92 DCHECK_GT(denominator, int64_t{0});
93 if (!WillProductOverflow(numerator_1, numerator_2)) {
94 const int64_t numerator = numerator_1 * numerator_2;
96 const int64_t result = numerator / denominator;
100 (
static_cast<double>(numerator_1) *
static_cast<double>(numerator_2)) /
101 static_cast<double>(denominator);
103 const int64_t result =
static_cast<int64_t
>(
floor(ratio + 0.5));
117 profit_upper_bound_(
std::numeric_limits<int64_t>::max()),
118 next_item_id_(kNoSelection) {}
123 : from_(
from), via_(nullptr), to_(
to) {}
128 CHECK_EQ(node_from->
depth(), node_to->
depth());
131 while (node_from != node_to) {
132 node_from = node_from->
parent();
133 node_to = node_to->
parent();
141 while (current_node->
depth() > depth) {
142 current_node = current_node->
parent();
151 is_bound_.assign(number_of_items,
false);
152 is_in_.assign(number_of_items,
false);
159 is_bound_[assignment.
item_id] =
false;
161 if (is_bound_[assignment.
item_id] &&
165 is_bound_[assignment.
item_id] =
true;
175 profit_lower_bound_(0),
176 profit_upper_bound_(
std::numeric_limits<int64_t>::max()),
182 const std::vector<int64_t>& weights) {
183 const int number_of_items = profits.size();
185 for (
int i = 0; i < number_of_items; ++i) {
186 items_[i] =
new KnapsackItem(i, weights[i], profits[i]);
189 profit_lower_bound_ = std::numeric_limits<int64_t>::min();
190 profit_upper_bound_ = std::numeric_limits<int64_t>::max();
196 if (assignment.
is_in) {
198 current_profit_ -= items_[assignment.
item_id]->profit;
200 current_profit_ += items_[assignment.
item_id]->profit;
207 bool has_one_propagator, std::vector<bool>*
solution)
const {
210 const int item_id = item->id;
211 (*solution)[item_id] = state_.is_bound(item_id) && state_.is_in(item_id);
213 if (has_one_propagator) {
223 consumed_capacity_(0),
224 break_item_id_(kNoSelection),
234 break_item_id_ = kNoSelection;
236 int64_t remaining_capacity = capacity_ - consumed_capacity_;
237 int break_sorted_item_id = kNoSelection;
238 const int number_of_sorted_items = sorted_items_.size();
239 for (
int sorted_id = 0; sorted_id < number_of_sorted_items; ++sorted_id) {
240 const KnapsackItem*
const item = sorted_items_[sorted_id];
241 if (!
state().is_bound(item->
id)) {
242 break_item_id_ = item->
id;
244 if (remaining_capacity >= item->
weight) {
245 remaining_capacity -= item->
weight;
248 break_sorted_item_id = sorted_id;
256 if (break_sorted_item_id != kNoSelection) {
257 const int64_t additional_profit =
258 GetAdditionalProfit(remaining_capacity, break_sorted_item_id);
264 consumed_capacity_ = 0;
265 break_item_id_ = kNoSelection;
266 sorted_items_ =
items();
269 profit_max_ = std::max(profit_max_, item->profit);
272 CompareKnapsackItemsInDecreasingEfficiencyOrder compare_object(profit_max_);
273 std::stable_sort(sorted_items_.begin(), sorted_items_.end(), compare_object);
279 if (assignment.
is_in) {
281 consumed_capacity_ -=
items()[assignment.
item_id]->weight;
283 consumed_capacity_ +=
items()[assignment.
item_id]->weight;
284 if (consumed_capacity_ > capacity_) {
293 std::vector<bool>*
solution)
const {
295 int64_t remaining_capacity = capacity_ - consumed_capacity_;
297 if (!
state().is_bound(item->id)) {
298 if (remaining_capacity >= item->weight) {
299 remaining_capacity -= item->weight;
300 (*solution)[item->id] =
true;
308int64_t KnapsackCapacityPropagator::GetAdditionalProfit(
309 int64_t remaining_capacity,
int break_item_id)
const {
310 const int after_break_item_id = break_item_id + 1;
311 int64_t additional_profit_when_no_break_item = 0;
312 if (after_break_item_id < sorted_items_.size()) {
315 const int64_t next_weight = sorted_items_[after_break_item_id]->weight;
316 const int64_t next_profit = sorted_items_[after_break_item_id]->profit;
317 additional_profit_when_no_break_item =
318 UpperBoundOfRatio(remaining_capacity, next_profit, next_weight);
321 const int before_break_item_id = break_item_id - 1;
322 int64_t additional_profit_when_break_item = 0;
323 if (before_break_item_id >= 0) {
324 const int64_t previous_weight = sorted_items_[before_break_item_id]->weight;
328 if (previous_weight != 0) {
329 const int64_t previous_profit =
330 sorted_items_[before_break_item_id]->profit;
331 const int64_t overused_capacity =
332 sorted_items_[break_item_id]->weight - remaining_capacity;
333 const int64_t ratio = UpperBoundOfRatio(overused_capacity,
334 previous_profit, previous_weight);
335 additional_profit_when_break_item =
336 sorted_items_[break_item_id]->profit - ratio;
340 const int64_t additional_profit = std::max(
341 additional_profit_when_no_break_item, additional_profit_when_break_item);
342 CHECK_GE(additional_profit, 0);
343 return additional_profit;
350 primary_propagator_id_(kPrimaryPropagatorId),
353 best_solution_profit_(0),
359 const std::vector<int64_t>& profits,
360 const std::vector<std::vector<int64_t>>& weights,
361 const std::vector<int64_t>& capacities) {
362 CHECK_EQ(capacities.size(), weights.size());
365 const int number_of_items = profits.size();
366 const int number_of_dimensions = weights.size();
367 state_.Init(number_of_items);
368 best_solution_.assign(number_of_items,
false);
369 for (
int i = 0; i < number_of_dimensions; ++i) {
370 CHECK_EQ(number_of_items, weights[i].size());
374 propagator->
Init(profits, weights[i]);
375 propagators_.push_back(propagator);
377 primary_propagator_id_ = kPrimaryPropagatorId;
381 int item_id,
bool is_item_in, int64_t* lower_bound, int64_t* upper_bound) {
382 CHECK(lower_bound !=
nullptr);
383 CHECK(upper_bound !=
nullptr);
385 const bool fail = !IncrementalUpdate(
false, assignment);
392 ? propagators_[primary_propagator_id_]->profit_lower_bound()
394 *upper_bound = GetAggregatedProfitUpperBound();
397 const bool fail_revert = !IncrementalUpdate(
true, assignment);
405 double time_limit_in_second,
406 bool* is_solution_optimal) {
407 DCHECK(time_limit !=
nullptr);
408 DCHECK(is_solution_optimal !=
nullptr);
409 best_solution_profit_ = 0LL;
410 *is_solution_optimal =
true;
412 SearchQueue search_queue;
418 search_nodes_.push_back(root_node);
420 if (MakeNewNode(*root_node,
false)) {
421 search_queue.push(search_nodes_.back());
423 if (MakeNewNode(*root_node,
true)) {
424 search_queue.push(search_nodes_.back());
428 while (!search_queue.empty() &&
429 search_queue.top()->profit_upper_bound() > best_solution_profit_) {
431 *is_solution_optimal =
false;
437 if (node != current_node) {
440 const bool no_fail = UpdatePropagators(path);
442 CHECK_EQ(no_fail,
true);
445 if (MakeNewNode(*node,
false)) {
446 search_queue.push(search_nodes_.back());
448 if (MakeNewNode(*node,
true)) {
449 search_queue.push(search_nodes_.back());
452 return best_solution_profit_;
455void KnapsackGenericSolver::Clear() {
461bool KnapsackGenericSolver::UpdatePropagators(
const KnapsackSearchPath& path) {
464 const KnapsackSearchNode* node = &path.from();
465 const KnapsackSearchNode* via = &path.via();
466 while (node != via) {
467 no_fail = IncrementalUpdate(
true, node->assignment()) && no_fail;
468 node = node->parent();
472 while (node != via) {
473 no_fail = IncrementalUpdate(
false, node->assignment()) && no_fail;
474 node = node->parent();
479int64_t KnapsackGenericSolver::GetAggregatedProfitUpperBound()
const {
480 int64_t upper_bound = std::numeric_limits<int64_t>::max();
481 for (KnapsackPropagator*
const prop : propagators_) {
482 prop->ComputeProfitBounds();
483 const int64_t propagator_upper_bound = prop->profit_upper_bound();
484 upper_bound = std::min(upper_bound, propagator_upper_bound);
491 if (node.next_item_id() == kNoSelection) {
494 KnapsackAssignment assignment(node.next_item_id(), is_in);
495 KnapsackSearchNode new_node(&node, assignment);
497 KnapsackSearchPath path(node, new_node);
499 const bool no_fail = UpdatePropagators(path);
501 new_node.set_current_profit(GetCurrentProfit());
502 new_node.set_profit_upper_bound(GetAggregatedProfitUpperBound());
503 new_node.set_next_item_id(GetNextItemId());
504 UpdateBestSolution();
508 KnapsackSearchPath revert_path(new_node, node);
510 UpdatePropagators(revert_path);
512 if (!no_fail || new_node.profit_upper_bound() < best_solution_profit_) {
517 KnapsackSearchNode* relevant_node =
new KnapsackSearchNode(&node, assignment);
518 relevant_node->set_current_profit(new_node.current_profit());
519 relevant_node->set_profit_upper_bound(new_node.profit_upper_bound());
520 relevant_node->set_next_item_id(new_node.next_item_id());
521 search_nodes_.push_back(relevant_node);
526bool KnapsackGenericSolver::IncrementalUpdate(
530 bool no_fail = state_.UpdateState(revert, assignment);
531 for (KnapsackPropagator*
const prop : propagators_) {
532 no_fail = prop->Update(revert, assignment) && no_fail;
537void KnapsackGenericSolver::UpdateBestSolution() {
538 const int64_t profit_lower_bound =
540 ? propagators_[primary_propagator_id_]->profit_lower_bound()
541 : propagators_[primary_propagator_id_]->current_profit();
543 if (best_solution_profit_ < profit_lower_bound) {
544 best_solution_profit_ = profit_lower_bound;
545 propagators_[primary_propagator_id_]->CopyCurrentStateToSolution(
546 HasOnePropagator(), &best_solution_);
564 void Init(
const std::vector<int64_t>& profits,
565 const std::vector<std::vector<int64_t>>& weights,
566 const std::vector<int64_t>& capacities)
override;
570 bool* is_solution_optimal)
override;
574 return (best_solution_ &
OneBit32(item_id)) != 0U;
579 int64_t profits_weights_[kMaxNumberOfBruteForceItems * 2];
581 int64_t best_solution_profit_;
582 uint32_t best_solution_;
586 absl::string_view solver_name)
590 best_solution_profit_(0LL),
591 best_solution_(0U) {}
594 const std::vector<int64_t>& profits,
595 const std::vector<std::vector<int64_t>>& weights,
596 const std::vector<int64_t>& capacities) {
598 CHECK_EQ(weights.size(), 1)
599 <<
"Brute force solver only works with one dimension.";
600 CHECK_EQ(capacities.size(), weights.size());
602 num_items_ = profits.size();
603 CHECK_EQ(num_items_, weights.at(0).size());
604 CHECK_LE(num_items_, kMaxNumberOfBruteForceItems)
605 <<
"To use KnapsackBruteForceSolver the number of items should be "
606 <<
"less than " << kMaxNumberOfBruteForceItems
607 <<
". Current value: " << num_items_ <<
".";
609 for (
int i = 0; i < num_items_; ++i) {
610 profits_weights_[i * 2] = profits.at(i);
611 profits_weights_[i * 2 + 1] = weights.at(0).at(i);
613 capacity_ = capacities.at(0);
617 double time_limit_in_second,
618 bool* is_solution_optimal) {
619 DCHECK(is_solution_optimal !=
nullptr);
620 *is_solution_optimal =
true;
621 best_solution_profit_ = 0LL;
624 const uint32_t num_states =
OneBit32(num_items_);
625 uint32_t prev_state = 0U;
626 uint64_t sum_profit = 0ULL;
627 uint64_t sum_weight = 0ULL;
628 uint32_t diff_state = 0U;
629 uint32_t local_state = 0U;
633 for (uint32_t state = 1U; state < num_states; ++state, ++prev_state) {
634 diff_state = state ^ prev_state;
638 if (diff_state & 1U) {
639 if (local_state & 1U) {
640 sum_profit += profits_weights_[item_id];
641 sum_weight += profits_weights_[item_id + 1];
642 CHECK_LT(item_id + 1, 2 * num_items_);
644 sum_profit -= profits_weights_[item_id];
645 sum_weight -= profits_weights_[item_id + 1];
646 CHECK_LT(item_id + 1, 2 * num_items_);
650 local_state = local_state >> 1;
651 diff_state = diff_state >> 1;
654 if (sum_weight <= capacity_ && best_solution_profit_ < sum_profit) {
655 best_solution_profit_ = sum_profit;
656 best_solution_ = state;
660 return best_solution_profit_;
676 static_cast<double>(_weight)
677 : static_cast<double>(_profit_max)) {}
694 void Init(
const std::vector<int64_t>& profits,
695 const std::vector<std::vector<int64_t>>& weights,
696 const std::vector<int64_t>& capacities)
override;
700 bool* is_solution_optimal)
override;
704 return (best_solution_ &
OneBit64(item_id)) != 0ULL;
708 int GetBreakItemId(int64_t capacity)
const;
709 void GetLowerAndUpperBound(int64_t* lower_bound, int64_t* upper_bound)
const;
710 void GoToNextState(
bool has_failed);
711 void BuildBestSolution();
713 std::vector<KnapsackItemWithEfficiency> sorted_items_;
714 std::vector<int64_t> sum_profits_;
715 std::vector<int64_t> sum_weights_;
720 int64_t best_solution_profit_;
721 uint64_t best_solution_;
722 int best_solution_depth_;
725 int64_t state_weight_;
727 int64_t rejected_items_profit_;
729 int64_t rejected_items_weight_;
748 best_solution_profit_(0LL),
749 best_solution_(0ULL),
750 best_solution_depth_(0),
752 rejected_items_profit_(0LL),
753 rejected_items_weight_(0LL) {}
756 const std::vector<int64_t>& profits,
757 const std::vector<std::vector<int64_t>>& weights,
758 const std::vector<int64_t>& capacities) {
759 CHECK_EQ(weights.size(), 1)
760 <<
"Brute force solver only works with one dimension.";
761 CHECK_EQ(capacities.size(), weights.size());
763 sorted_items_.clear();
764 sum_profits_.clear();
765 sum_weights_.clear();
767 capacity_ = capacities[0];
768 const int num_items = profits.size();
769 CHECK_LE(num_items, kMaxNumberOf64Items)
770 <<
"To use Knapsack64ItemsSolver the number of items should be "
771 <<
"less than " << kMaxNumberOf64Items <<
". Current value: " << num_items
773 int64_t profit_max = *std::max_element(profits.begin(), profits.end());
775 for (
int i = 0; i < num_items; ++i) {
776 sorted_items_.push_back(
780 std::sort(sorted_items_.begin(), sorted_items_.end(),
783 int64_t sum_profit = 0;
784 int64_t sum_weight = 0;
785 sum_profits_.push_back(sum_profit);
786 sum_weights_.push_back(sum_weight);
787 for (
int i = 0; i < num_items; ++i) {
788 sum_profit += sorted_items_[i].profit;
789 sum_weight += sorted_items_[i].weight;
791 sum_profits_.push_back(sum_profit);
792 sum_weights_.push_back(sum_weight);
797 double time_limit_in_second,
798 bool* is_solution_optimal) {
799 DCHECK(is_solution_optimal !=
nullptr);
800 *is_solution_optimal =
true;
801 const int num_items = sorted_items_.size();
804 state_weight_ = sorted_items_[0].weight;
805 rejected_items_profit_ = 0LL;
806 rejected_items_weight_ = 0LL;
807 best_solution_profit_ = 0LL;
808 best_solution_ = 0ULL;
809 best_solution_depth_ = 0;
811 int64_t lower_bound = 0LL;
812 int64_t upper_bound = 0LL;
814 while (state_depth_ >= 0) {
816 if (state_weight_ > capacity_ || state_depth_ >= num_items) {
819 GetLowerAndUpperBound(&lower_bound, &upper_bound);
820 if (best_solution_profit_ < lower_bound) {
821 best_solution_profit_ = lower_bound;
822 best_solution_ = state_;
823 best_solution_depth_ = state_depth_;
826 fail = fail || best_solution_profit_ >= upper_bound;
831 return best_solution_profit_;
834int Knapsack64ItemsSolver::GetBreakItemId(int64_t capacity)
const {
835 std::vector<int64_t>::const_iterator binary_search_iterator =
836 std::upper_bound(sum_weights_.begin(), sum_weights_.end(), capacity);
837 return static_cast<int>(binary_search_iterator - sum_weights_.begin()) - 1;
847void Knapsack64ItemsSolver::GetLowerAndUpperBound(int64_t* lower_bound,
848 int64_t* upper_bound)
const {
849 const int64_t available_capacity = capacity_ + rejected_items_weight_;
850 const int break_item_id = GetBreakItemId(available_capacity);
851 const int num_items = sorted_items_.size();
852 if (break_item_id >= num_items) {
853 *lower_bound = sum_profits_[num_items] - rejected_items_profit_;
854 *upper_bound = *lower_bound;
858 *lower_bound = sum_profits_[break_item_id] - rejected_items_profit_;
859 *upper_bound = *lower_bound;
860 const int64_t consumed_capacity = sum_weights_[break_item_id];
861 const int64_t remaining_capacity = available_capacity - consumed_capacity;
862 const double efficiency = sorted_items_[break_item_id].efficiency;
863 const int64_t additional_profit =
864 static_cast<int64_t
>(remaining_capacity * efficiency);
865 *upper_bound += additional_profit;
873void Knapsack64ItemsSolver::GoToNextState(
bool has_failed) {
874 uint64_t mask =
OneBit64(state_depth_);
877 state_ = state_ | (mask << 1);
878 state_weight_ += sorted_items_[state_depth_].weight;
881 while ((state_ & mask) == 0ULL && state_depth_ >= 0) {
882 const KnapsackItemWithEfficiency& item = sorted_items_[state_depth_];
883 rejected_items_profit_ -= item.profit;
884 rejected_items_weight_ -= item.weight;
890 state_ = state_ & ~mask;
891 const KnapsackItemWithEfficiency& item = sorted_items_[state_depth_];
892 rejected_items_profit_ += item.profit;
893 rejected_items_weight_ += item.weight;
894 state_weight_ -= item.weight;
899void Knapsack64ItemsSolver::BuildBestSolution() {
900 int64_t remaining_capacity = capacity_;
901 int64_t check_profit = 0LL;
905 for (
int i = 0;
i <= best_solution_depth_; ++
i) {
907 remaining_capacity -= sorted_items_[
i].weight;
908 check_profit += sorted_items_[
i].profit;
913 const int num_items = sorted_items_.size();
914 for (
int i = best_solution_depth_ + 1;
i < num_items; ++
i) {
915 int64_t weight = sorted_items_[
i].weight;
916 if (remaining_capacity >= weight) {
917 remaining_capacity -= weight;
918 check_profit += sorted_items_[
i].profit;
919 best_solution_ = best_solution_ |
OneBit64(i);
921 best_solution_ = best_solution_ & ~OneBit64(i);
924 CHECK_EQ(best_solution_profit_, check_profit);
930 uint64_t tmp_solution = 0ULL;
931 for (
int i = 0;
i < num_items; ++
i) {
933 const int original_id = sorted_items_[
i].id;
934 tmp_solution = tmp_solution |
OneBit64(original_id);
938 best_solution_ = tmp_solution;
953 void Init(
const std::vector<int64_t>& profits,
954 const std::vector<std::vector<int64_t>>& weights,
955 const std::vector<int64_t>& capacities)
override;
959 bool* is_solution_optimal)
override;
963 return best_solution_.at(item_id);
967 int64_t SolveSubProblem(int64_t capacity,
int num_items);
969 std::vector<int64_t> profits_;
970 std::vector<int64_t> weights_;
972 std::vector<int64_t> computed_profits_;
973 std::vector<int> selected_item_ids_;
974 std::vector<bool> best_solution_;
979 absl::string_view solver_name)
985 selected_item_ids_(),
989 const std::vector<int64_t>& profits,
990 const std::vector<std::vector<int64_t>>& weights,
991 const std::vector<int64_t>& capacities) {
992 CHECK_EQ(weights.size(), 1)
993 <<
"Current implementation of the dynamic programming solver only deals"
994 <<
" with one dimension.";
995 CHECK_EQ(capacities.size(), weights.size());
998 weights_ = weights[0];
999 capacity_ = capacities[0];
1002int64_t KnapsackDynamicProgrammingSolver::SolveSubProblem(int64_t capacity,
1004 const int64_t capacity_plus_1 = capacity + 1;
1005 std::fill_n(selected_item_ids_.begin(), capacity_plus_1, 0);
1006 std::fill_n(computed_profits_.begin(), capacity_plus_1, int64_t{0});
1007 for (
int item_id = 0; item_id < num_items; ++item_id) {
1008 const int64_t item_weight = weights_[item_id];
1009 const int64_t item_profit = profits_[item_id];
1010 for (int64_t used_capacity = capacity; used_capacity >= item_weight;
1012 if (computed_profits_[used_capacity - item_weight] + item_profit >
1013 computed_profits_[used_capacity]) {
1014 computed_profits_[used_capacity] =
1015 computed_profits_[used_capacity - item_weight] + item_profit;
1016 selected_item_ids_[used_capacity] = item_id;
1020 return selected_item_ids_.at(capacity);
1024 double time_limit_in_second,
1025 bool* is_solution_optimal) {
1026 DCHECK(is_solution_optimal !=
nullptr);
1027 *is_solution_optimal =
true;
1028 const int64_t capacity_plus_1 = capacity_ + 1;
1029 selected_item_ids_.assign(capacity_plus_1, 0);
1030 computed_profits_.assign(capacity_plus_1, 0LL);
1032 int64_t remaining_capacity = capacity_;
1033 int num_items = profits_.size();
1034 best_solution_.assign(num_items,
false);
1036 while (remaining_capacity > 0 && num_items > 0) {
1037 const int selected_item_id = SolveSubProblem(remaining_capacity, num_items);
1038 remaining_capacity -= weights_[selected_item_id];
1039 num_items = selected_item_id;
1040 if (remaining_capacity >= 0) {
1041 best_solution_[selected_item_id] =
true;
1045 return computed_profits_[capacity_];
1061 void Init(
const std::vector<int64_t>& profits,
1062 const std::vector<std::vector<int64_t>>& weights,
1063 const std::vector<int64_t>& capacities)
override;
1066 int64_t
Solve(
TimeLimit* time_limit,
double time_limit_in_second,
1067 bool* is_solution_optimal)
override;
1071 return best_solution_.at(item_id);
1076 void SolveSubProblem(
bool first_storage, int64_t capacity,
int start_item,
1080 int64_t DivideAndConquer(int64_t capacity,
int start_item,
int end_item);
1082 std::vector<int64_t> profits_;
1083 std::vector<int64_t> weights_;
1085 std::vector<int64_t> computed_profits_storage1_;
1086 std::vector<int64_t> computed_profits_storage2_;
1087 std::vector<bool> best_solution_;
1092 absl::string_view solver_name)
1097 computed_profits_storage1_(),
1098 computed_profits_storage2_(),
1102 const std::vector<int64_t>& profits,
1103 const std::vector<std::vector<int64_t>>& weights,
1104 const std::vector<int64_t>& capacities) {
1105 CHECK_EQ(weights.size(), 1)
1106 <<
"Current implementation of the divide and conquer solver only deals"
1107 <<
" with one dimension.";
1108 CHECK_EQ(capacities.size(), weights.size());
1111 weights_ = weights[0];
1112 capacity_ = capacities[0];
1115void KnapsackDivideAndConquerSolver::SolveSubProblem(
bool first_storage,
1119 std::vector<int64_t>& computed_profits_storage =
1120 (first_storage) ? computed_profits_storage1_ : computed_profits_storage2_;
1121 const int64_t capacity_plus_1 = capacity + 1;
1122 std::fill_n(computed_profits_storage.begin(), capacity_plus_1, 0LL);
1123 for (
int item_id = start_item; item_id < end_item; ++item_id) {
1124 const int64_t item_weight = weights_[item_id];
1125 const int64_t item_profit = profits_[item_id];
1126 for (int64_t used_capacity = capacity; used_capacity >= item_weight;
1128 if (computed_profits_storage[used_capacity - item_weight] + item_profit >
1129 computed_profits_storage[used_capacity]) {
1130 computed_profits_storage[used_capacity] =
1131 computed_profits_storage[used_capacity - item_weight] + item_profit;
1137int64_t KnapsackDivideAndConquerSolver::DivideAndConquer(int64_t capacity,
1140 int item_boundary = start_item + ((end_item - start_item) / 2);
1142 SolveSubProblem(
true, capacity, start_item, item_boundary);
1143 SolveSubProblem(
false, capacity, item_boundary, end_item);
1145 int64_t max_solution = 0, capacity1 = 0, capacity2 = 0;
1147 for (int64_t capacity_id = 0; capacity_id <= capacity; capacity_id++) {
1148 if ((computed_profits_storage1_[capacity_id] +
1149 computed_profits_storage2_[(capacity - capacity_id)]) > max_solution) {
1150 capacity1 = capacity_id;
1151 capacity2 = capacity - capacity_id;
1152 max_solution = (computed_profits_storage1_[capacity_id] +
1153 computed_profits_storage2_[(capacity - capacity_id)]);
1157 if ((item_boundary - start_item) == 1) {
1158 if (weights_[start_item] <= capacity1) best_solution_[start_item] =
true;
1159 }
else if ((item_boundary - start_item) > 1) {
1160 DivideAndConquer(capacity1, start_item, item_boundary);
1163 if ((end_item - item_boundary) == 1) {
1164 if (weights_[item_boundary] <= capacity2)
1165 best_solution_[item_boundary] =
true;
1166 }
else if ((end_item - item_boundary) > 1) {
1167 DivideAndConquer(capacity2, item_boundary, end_item);
1169 return max_solution;
1173 double time_limit_in_second,
1174 bool* is_solution_optimal) {
1175 DCHECK(is_solution_optimal !=
nullptr);
1176 *is_solution_optimal =
true;
1177 const int64_t capacity_plus_1 = capacity_ + 1;
1178 computed_profits_storage1_.assign(capacity_plus_1, 0LL);
1179 computed_profits_storage2_.assign(capacity_plus_1, 0LL);
1180 best_solution_.assign(profits_.size(),
false);
1182 return DivideAndConquer(capacity_, 0, profits_.size());
1188 absl::string_view solver_name);
1191 void Init(
const std::vector<int64_t>& profits,
1192 const std::vector<std::vector<int64_t>>& weights,
1193 const std::vector<int64_t>& capacities)
override;
1196 int64_t
Solve(
TimeLimit* time_limit,
double time_limit_in_second,
1197 bool* is_solution_optimal)
override;
1201 return best_solution_.at(item_id);
1206 std::vector<int64_t> profits_;
1207 std::vector<std::vector<int64_t>> weights_;
1208 std::vector<int64_t> capacities_;
1209 std::vector<bool> best_solution_;
1214 absl::string_view solver_name)
1216 problem_type_(problem_type),
1223 const std::vector<std::vector<int64_t>>& weights,
1224 const std::vector<int64_t>& capacities) {
1227 capacities_ = capacities;
1231 double time_limit_in_second,
1232 bool* is_solution_optimal) {
1233 DCHECK(is_solution_optimal !=
nullptr);
1234 *is_solution_optimal =
true;
1237 const int num_items = profits_.size();
1238 std::vector<MPVariable*> variables;
1242 const int num_dimensions = capacities_.size();
1243 CHECK(weights_.size() == num_dimensions)
1244 <<
"Weights should be vector of num_dimensions (" << num_dimensions
1245 <<
") vectors of size num_items (" << num_items <<
").";
1246 for (
int i = 0; i < num_dimensions; ++i) {
1248 for (
int j = 0; j < num_items; ++j) {
1257 for (
int j = 0; j < num_items; ++j) {
1263 solver.
SetTimeLimit(absl::Seconds(time_limit_in_second));
1266 best_solution_.assign(num_items,
false);
1269 const float kRoundNear = 0.5;
1270 for (
int j = 0; j < num_items; ++j) {
1271 const double value = variables.at(j)->solution_value();
1272 best_solution_.at(j) = value >= kRoundNear;
1277 return -objective->
Value() + kRoundNear;
1279 *is_solution_optimal =
false;
1290 void Init(
const std::vector<int64_t>& profits,
1291 const std::vector<std::vector<int64_t>>& weights,
1292 const std::vector<int64_t>& capacities)
override;
1295 int64_t
Solve(
TimeLimit* time_limit,
double time_limit_in_seconds,
1296 bool* is_solution_optimal)
override;
1300 return best_solution_.at(item_id);
1304 std::vector<int64_t> profits_;
1305 std::vector<std::vector<int64_t>> weights_;
1306 std::vector<int64_t> capacities_;
1307 std::vector<bool> best_solution_;
1318 const std::vector<std::vector<int64_t>>& weights,
1319 const std::vector<int64_t>& capacities) {
1322 capacities_ = capacities;
1326 double time_limit_in_seconds,
1327 bool* is_solution_optimal) {
1328 DCHECK(is_solution_optimal !=
nullptr);
1329 *is_solution_optimal =
true;
1334 const int num_items = profits_.size();
1335 std::vector<sat::BoolVar> variables;
1336 variables.reserve(num_items);
1337 for (
int i = 0; i < num_items; ++i) {
1342 const int num_dimensions = capacities_.size();
1343 CHECK(weights_.size() == num_dimensions)
1344 <<
"Weights should be vector of num_dimensions (" << num_dimensions
1345 <<
") vectors of size num_items (" << num_items <<
").";
1346 for (
int i = 0; i < num_dimensions; ++i) {
1348 for (
int j = 0; j < num_items; ++j) {
1349 expr += variables.at(j) * weights_.at(i).at(j);
1351 model.AddLessOrEqual(expr, capacities_.at(i));
1356 for (
int j = 0; j < num_items; ++j) {
1357 objective += variables.at(j) * profits_.at(j);
1359 model.Maximize(objective);
1369 best_solution_.assign(num_items,
false);
1372 for (
int j = 0; j < num_items; ++j) {
1373 best_solution_.at(j) = SolutionBooleanValue(response, variables.at(j));
1379 *is_solution_optimal =
false;
1390 const std::string& solver_name)
1394 mapping_reduced_item_id_(),
1395 is_problem_solved_(false),
1396 additional_profit_(0LL),
1397 use_reduction_(true),
1398 time_limit_seconds_(
std::numeric_limits<double>::infinity()) {
1399 switch (solver_type) {
1401 solver_ = std::make_unique<KnapsackBruteForceSolver>(solver_name);
1404 solver_ = std::make_unique<Knapsack64ItemsSolver>(solver_name);
1407 solver_ = std::make_unique<KnapsackDynamicProgrammingSolver>(solver_name);
1410 solver_ = std::make_unique<KnapsackGenericSolver>(solver_name);
1413 solver_ = std::make_unique<KnapsackDivideAndConquerSolver>(solver_name);
1416 solver_ = std::make_unique<KnapsackMIPSolver>(
1420 solver_ = std::make_unique<KnapsackMIPSolver>(
1424 solver_ = std::make_unique<KnapsackMIPSolver>(
1428 solver_ = std::make_unique<KnapsackMIPSolver>(
1432 solver_ = std::make_unique<KnapsackCpSat>(solver_name);
1435 LOG(FATAL) <<
"Unknown knapsack solver type.";
1442 const std::vector<std::vector<int64_t>>& weights,
1443 const std::vector<int64_t>& capacities) {
1444 for (
const std::vector<int64_t>&
w : weights) {
1445 CHECK_EQ(profits.size(),
w.size())
1446 <<
"Profits and inner weights must have the same size (#items)";
1448 CHECK_EQ(capacities.size(), weights.size())
1449 <<
"Capacities and weights must have the same size (#bins)";
1450 time_limit_ = std::make_unique<TimeLimit>(time_limit_seconds_);
1451 is_solution_optimal_ =
false;
1452 additional_profit_ = 0LL;
1453 is_problem_solved_ =
false;
1455 const int num_items = profits.size();
1456 std::vector<std::vector<int64_t>> reduced_weights;
1457 std::vector<int64_t> reduced_capacities;
1458 if (use_reduction_) {
1459 const int num_reduced_items = ReduceCapacities(
1460 num_items, weights, capacities, &reduced_weights, &reduced_capacities);
1461 if (num_reduced_items > 0) {
1462 ComputeAdditionalProfit(profits);
1465 reduced_weights = weights;
1466 reduced_capacities = capacities;
1468 if (!is_problem_solved_) {
1469 solver_->Init(profits, reduced_weights, reduced_capacities);
1470 if (use_reduction_) {
1471 const int num_reduced_items = ReduceProblem(num_items);
1473 if (num_reduced_items > 0) {
1474 ComputeAdditionalProfit(profits);
1477 if (num_reduced_items > 0 && num_reduced_items < num_items) {
1478 InitReducedProblem(profits, reduced_weights, reduced_capacities);
1482 if (is_problem_solved_) {
1483 is_solution_optimal_ =
true;
1487int KnapsackSolver::ReduceCapacities(
1488 int num_items,
const std::vector<std::vector<int64_t>>& weights,
1489 const std::vector<int64_t>& capacities,
1490 std::vector<std::vector<int64_t>>* reduced_weights,
1491 std::vector<int64_t>* reduced_capacities) {
1492 known_value_.assign(num_items,
false);
1493 best_solution_.assign(num_items,
false);
1494 mapping_reduced_item_id_.assign(num_items, 0);
1495 std::vector<bool> active_capacities(weights.size(),
true);
1496 int number_of_active_capacities = 0;
1497 for (
int i = 0; i < weights.size(); ++i) {
1498 int64_t max_weight = 0;
1499 for (int64_t weight : weights[i]) {
1500 max_weight += weight;
1502 if (max_weight <= capacities[i]) {
1503 active_capacities[i] =
false;
1505 ++number_of_active_capacities;
1508 reduced_weights->reserve(number_of_active_capacities);
1509 reduced_capacities->reserve(number_of_active_capacities);
1510 for (
int i = 0;
i < weights.size(); ++
i) {
1511 if (active_capacities[i]) {
1512 reduced_weights->push_back(weights[i]);
1513 reduced_capacities->push_back(capacities[i]);
1516 if (reduced_capacities->empty()) {
1519 for (
int item_id = 0; item_id < num_items; ++item_id) {
1520 known_value_[item_id] =
true;
1521 best_solution_[item_id] =
true;
1523 is_problem_solved_ =
true;
1531int KnapsackSolver::ReduceProblem(
int num_items) {
1532 known_value_.assign(num_items,
false);
1533 best_solution_.assign(num_items,
false);
1534 mapping_reduced_item_id_.assign(num_items, 0);
1535 additional_profit_ = 0LL;
1537 for (
int item_id = 0; item_id < num_items; ++item_id) {
1538 mapping_reduced_item_id_[item_id] = item_id;
1541 int64_t best_lower_bound = 0LL;
1542 std::vector<int64_t> J0_upper_bounds(num_items,
1543 std::numeric_limits<int64_t>::max());
1544 std::vector<int64_t> J1_upper_bounds(num_items,
1545 std::numeric_limits<int64_t>::max());
1546 for (
int item_id = 0; item_id < num_items; ++item_id) {
1547 if (time_limit_->LimitReached()) {
1550 int64_t lower_bound = 0LL;
1551 int64_t upper_bound = std::numeric_limits<int64_t>::max();
1552 solver_->GetLowerAndUpperBoundWhenItem(item_id,
false, &lower_bound,
1554 J1_upper_bounds.at(item_id) = upper_bound;
1555 best_lower_bound = std::max(best_lower_bound, lower_bound);
1557 solver_->GetLowerAndUpperBoundWhenItem(item_id,
true, &lower_bound,
1559 J0_upper_bounds.at(item_id) = upper_bound;
1560 best_lower_bound = std::max(best_lower_bound, lower_bound);
1563 int num_reduced_items = 0;
1564 for (
int item_id = 0; item_id < num_items; ++item_id) {
1565 if (best_lower_bound > J0_upper_bounds[item_id]) {
1566 known_value_[item_id] =
true;
1567 best_solution_[item_id] =
false;
1568 ++num_reduced_items;
1569 }
else if (best_lower_bound > J1_upper_bounds[item_id]) {
1570 known_value_[item_id] =
true;
1571 best_solution_[item_id] =
true;
1572 ++num_reduced_items;
1576 is_problem_solved_ = num_reduced_items == num_items;
1577 return num_reduced_items;
1580void KnapsackSolver::ComputeAdditionalProfit(
1581 const std::vector<int64_t>& profits) {
1582 const int num_items = profits.size();
1583 additional_profit_ = 0LL;
1584 for (
int item_id = 0; item_id < num_items; ++item_id) {
1585 if (known_value_[item_id] && best_solution_[item_id]) {
1586 additional_profit_ += profits[item_id];
1591void KnapsackSolver::InitReducedProblem(
1592 const std::vector<int64_t>& profits,
1593 const std::vector<std::vector<int64_t>>& weights,
1594 const std::vector<int64_t>& capacities) {
1595 const int num_items = profits.size();
1596 const int num_dimensions = capacities.size();
1598 std::vector<int64_t> reduced_profits;
1599 for (
int item_id = 0; item_id < num_items; ++item_id) {
1600 if (!known_value_[item_id]) {
1601 mapping_reduced_item_id_[item_id] = reduced_profits.size();
1602 reduced_profits.push_back(profits[item_id]);
1606 std::vector<std::vector<int64_t>> reduced_weights;
1607 std::vector<int64_t> reduced_capacities = capacities;
1608 for (
int dim = 0; dim < num_dimensions; ++dim) {
1609 const std::vector<int64_t>& one_dimension_weights = weights[dim];
1610 std::vector<int64_t> one_dimension_reduced_weights;
1611 for (
int item_id = 0; item_id < num_items; ++item_id) {
1612 if (known_value_[item_id]) {
1613 if (best_solution_[item_id]) {
1614 reduced_capacities[dim] -= one_dimension_weights[item_id];
1617 one_dimension_reduced_weights.push_back(one_dimension_weights[item_id]);
1620 reduced_weights.push_back(std::move(one_dimension_reduced_weights));
1622 solver_->Init(reduced_profits, reduced_weights, reduced_capacities);
1626 return additional_profit_ +
1627 ((is_problem_solved_)
1629 : solver_->Solve(time_limit_.get(), time_limit_seconds_,
1630 &is_solution_optimal_));
1634 const int mapped_item_id =
1635 (use_reduction_) ? mapping_reduced_item_id_[item_id] : item_id;
1636 return (use_reduction_ && known_value_[item_id])
1637 ? best_solution_[item_id]
1638 : solver_->best_solution(mapped_item_id);
1646 int64_t* lower_bound,
1647 int64_t* upper_bound) {
1648 CHECK(lower_bound !=
nullptr);
1649 CHECK(upper_bound !=
nullptr);
1651 *upper_bound = std::numeric_limits<int64_t>::max();
BaseKnapsackSolver(absl::string_view solver_name)
virtual void GetLowerAndUpperBoundWhenItem(int item_id, bool is_item_in, int64_t *lower_bound, int64_t *upper_bound)
virtual std::string GetName() const
Knapsack64ItemsSolver(absl::string_view solver_name)
bool best_solution(int item_id) const override
Returns true if the item 'item_id' is packed in the optimal knapsack.
int64_t Solve(TimeLimit *time_limit, double time_limit_in_second, bool *is_solution_optimal) override
Solves the problem and returns the profit of the optimal solution.
void Init(const std::vector< int64_t > &profits, const std::vector< std::vector< int64_t > > &weights, const std::vector< int64_t > &capacities) override
Initializes the solver and enters the problem to be solved.
KnapsackBruteForceSolver(absl::string_view solver_name)
void Init(const std::vector< int64_t > &profits, const std::vector< std::vector< int64_t > > &weights, const std::vector< int64_t > &capacities) override
Initializes the solver and enters the problem to be solved.
KnapsackBruteForceSolver & operator=(const KnapsackBruteForceSolver &)=delete
bool best_solution(int item_id) const override
Returns true if the item 'item_id' is packed in the optimal knapsack.
KnapsackBruteForceSolver(const KnapsackBruteForceSolver &)=delete
int64_t Solve(TimeLimit *time_limit, double time_limit_in_second, bool *is_solution_optimal) override
Solves the problem and returns the profit of the optimal solution.
KnapsackCapacityPropagator is a KnapsackPropagator used to enforce a capacity constraint.
void CopyCurrentStateToSolutionPropagator(std::vector< bool > *solution) const override
~KnapsackCapacityPropagator() override
KnapsackCapacityPropagator(const KnapsackState &state, int64_t capacity)
void InitPropagator() override
bool UpdatePropagator(bool revert, const KnapsackAssignment &assignment) override
void ComputeProfitBounds() override
void Init(const std::vector< int64_t > &profits, const std::vector< std::vector< int64_t > > &weights, const std::vector< int64_t > &capacities) override
Initializes the solver and enters the problem to be solved.
bool best_solution(int item_id) const override
Returns true if the item 'item_id' is packed in the optimal knapsack.
KnapsackCpSat(absl::string_view solver_name)
int64_t Solve(TimeLimit *time_limit, double time_limit_in_seconds, bool *is_solution_optimal) override
Solves the problem and returns the profit of the optimal solution.
int64_t Solve(TimeLimit *time_limit, double time_limit_in_second, bool *is_solution_optimal) override
Solves the problem and returns the profit of the optimal solution.
void Init(const std::vector< int64_t > &profits, const std::vector< std::vector< int64_t > > &weights, const std::vector< int64_t > &capacities) override
Initializes the solver and enters the problem to be solved.
KnapsackDivideAndConquerSolver(absl::string_view solver_name)
bool best_solution(int item_id) const override
Returns true if the item 'item_id' is packed in the optimal knapsack.
KnapsackDynamicProgrammingSolver(absl::string_view solver_name)
void Init(const std::vector< int64_t > &profits, const std::vector< std::vector< int64_t > > &weights, const std::vector< int64_t > &capacities) override
Initializes the solver and enters the problem to be solved.
bool best_solution(int item_id) const override
Returns true if the item 'item_id' is packed in the optimal knapsack.
int64_t Solve(TimeLimit *time_limit, double time_limit_in_second, bool *is_solution_optimal) override
Solves the problem and returns the profit of the optimal solution.
void GetLowerAndUpperBoundWhenItem(int item_id, bool is_item_in, int64_t *lower_bound, int64_t *upper_bound) override
~KnapsackGenericSolver() override
void Init(const std::vector< int64_t > &profits, const std::vector< std::vector< int64_t > > &weights, const std::vector< int64_t > &capacities) override
Initializes the solver and enters the problem to be solved.
KnapsackGenericSolver(const std::string &solver_name)
int64_t Solve(TimeLimit *time_limit, double time_limit_in_seconds, bool *is_solution_optimal) override
Solves the problem and returns the profit of the optimal solution.
KnapsackMIPSolver(MPSolver::OptimizationProblemType problem_type, absl::string_view solver_name)
void Init(const std::vector< int64_t > &profits, const std::vector< std::vector< int64_t > > &weights, const std::vector< int64_t > &capacities) override
Initializes the solver and enters the problem to be solved.
int64_t Solve(TimeLimit *time_limit, double time_limit_in_second, bool *is_solution_optimal) override
Solves the problem and returns the profit of the optimal solution.
bool best_solution(int item_id) const override
Returns true if the item 'item_id' is packed in the optimal knapsack.
void set_profit_upper_bound(int64_t profit)
void CopyCurrentStateToSolution(bool has_one_propagator, std::vector< bool > *solution) const
int64_t profit_lower_bound() const
bool Update(bool revert, const KnapsackAssignment &assignment)
void set_profit_lower_bound(int64_t profit)
void Init(const std::vector< int64_t > &profits, const std::vector< int64_t > &weights)
Initializes data structure and then calls InitPropagator.
const KnapsackState & state() const
KnapsackPropagator(const KnapsackState &state)
virtual ~KnapsackPropagator()
virtual void CopyCurrentStateToSolutionPropagator(std::vector< bool > *solution) const =0
virtual bool UpdatePropagator(bool revert, const KnapsackAssignment &assignment)=0
int64_t profit_upper_bound() const
const std::vector< KnapsackItemPtr > & items() const
virtual void InitPropagator()=0
int64_t current_profit() const
KnapsackSearchNode(const KnapsackSearchNode *parent, const KnapsackAssignment &assignment)
const KnapsackAssignment & assignment() const
void set_current_profit(int64_t profit)
void set_next_item_id(int id)
const KnapsackSearchNode * parent() const
void set_profit_upper_bound(int64_t profit)
KnapsackSearchPath(const KnapsackSearchNode &from, const KnapsackSearchNode &to)
const KnapsackSearchNode & to() const
const KnapsackSearchNode & from() const
const KnapsackSearchNode * MoveUpToDepth(const KnapsackSearchNode &node, int depth) const
bool BestSolutionContains(int item_id) const
Returns true if the item 'item_id' is packed in the optimal knapsack.
void Init(const std::vector< int64_t > &profits, const std::vector< std::vector< int64_t > > &weights, const std::vector< int64_t > &capacities)
Initializes the solver and enters the problem to be solved.
KnapsackSolver(const std::string &solver_name)
SolverType
Enum controlling which underlying algorithm is used.
@ KNAPSACK_DIVIDE_AND_CONQUER_SOLVER
@ KNAPSACK_BRUTE_FORCE_SOLVER
@ KNAPSACK_MULTIDIMENSION_XPRESS_MIP_SOLVER
@ KNAPSACK_MULTIDIMENSION_CP_SAT_SOLVER
@ KNAPSACK_DYNAMIC_PROGRAMMING_SOLVER
@ KNAPSACK_64ITEMS_SOLVER
@ KNAPSACK_MULTIDIMENSION_SCIP_MIP_SOLVER
@ KNAPSACK_MULTIDIMENSION_CPLEX_MIP_SOLVER
@ KNAPSACK_MULTIDIMENSION_CBC_MIP_SOLVER
@ KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER
virtual ~KnapsackSolver()
std::string GetName() const
int64_t Solve()
Solves the problem and returns the profit of the optimal solution.
KnapsackState represents a partial solution to the knapsack problem.
bool UpdateState(bool revert, const KnapsackAssignment &assignment)
void Init(int number_of_items)
void SetCoefficient(const MPVariable *var, double coeff)
A class to express a linear objective.
void SetCoefficient(const MPVariable *var, double coeff)
void SetMinimization()
Sets the optimization direction to minimize.
@ FEASIBLE
feasible, or stopped by limit.
ResultStatus Solve()
Solves the problem using the default parameter values.
@ SCIP_MIXED_INTEGER_PROGRAMMING
@ XPRESS_MIXED_INTEGER_PROGRAMMING
@ CBC_MIXED_INTEGER_PROGRAMMING
@ CPLEX_MIXED_INTEGER_PROGRAMMING
void SetTimeLimit(absl::Duration time_limit)
MPObjective * MutableObjective()
Returns the mutable objective object.
void SuppressOutput()
Suppresses solver logging.
MPConstraint * MakeRowConstraint(double lb, double ub)
void MakeBoolVarArray(int nb, const std::string &name, std::vector< MPVariable * > *vars)
Creates an array of boolean variables.
*Creates a Boolean variable BoolVar NewBoolVar()
*Sets the name of the model void SetName(absl::string_view name)
::operations_research::sat::CpSolverStatus status() const
double objective_value() const
void set_max_time_in_seconds(double value)
void set_num_workers(::int32_t value)
void STLDeleteElements(T *container)
CpSolverResponse SolveWithParameters(const CpModelProto &model_proto, const SatParameters ¶ms)
Solves the given CpModelProto with the given parameters.
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
bool CompareKnapsackItemWithEfficiencyInDecreasingEfficiencyOrder(const KnapsackItemWithEfficiency &item1, const KnapsackItemWithEfficiency &item2)
uint32_t OneBit32(int pos)
KnapsackItem * KnapsackItemPtr
uint64_t OneBit64(int pos)
int MostSignificantBitPosition64(uint64_t n)
trees with all degrees equal w the current value of w
KnapsackItemWithEfficiency(int _id, int64_t _profit, int64_t _weight, int64_t _profit_max)