/
murrayc_dp_bottom_up_knapsack.cc
188 lines (153 loc) · 5.73 KB
/
murrayc_dp_bottom_up_knapsack.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/* Copyright (C) 2015, 2016 Murray Cumming
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/
*/
#include <cassert>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <limits>
#include <string>
#include <vector>
#include <murraycdp/dp_bottom_up_base.h>
class Item {
public:
using type_value = long long;
using type_weight = long long;
Item() : value(0), weight(0) {}
Item(type_value value_in, type_weight weight_in)
: value(value_in), weight(weight_in){};
Item(const Item& src) = default;
Item&
operator=(const Item& src) = default;
Item(Item&& src) noexcept = default;
Item&
operator=(Item&& src) noexcept = default;
type_value value;
type_weight weight;
};
class SubSolution {
public:
using type_vec_items = std::vector<Item>;
using type_value = Item::type_value;
// static constexpr type_value COIN_COUNT_INFINITY =
// std::numeric_limits<type_value>::max();
SubSolution() : value(0) {}
explicit SubSolution(type_value value_in) : value(value_in) {}
SubSolution(const SubSolution& src) = default;
SubSolution&
operator=(const SubSolution& src) = default;
SubSolution(SubSolution&& src) noexcept = default;
SubSolution&
operator=(SubSolution&& src) noexcept = default;
type_value value;
type_vec_items solution;
};
class DpKnapsack
: public murraycdp::DpBottomUpBase<2, // count of subproblems to keep.
SubSolution, SubSolution::type_vec_items::size_type, Item::type_weight> {
public:
using type_value = Item::type_value;
using type_weight = Item::type_weight;
using type_vec_items = SubSolution::type_vec_items;
using type_size = type_vec_items::size_type;
DpKnapsack(const type_vec_items& items, type_weight weight_capacity)
: DpBottomUpBase(items.size() + 1, weight_capacity + 1),
items_(items),
weight_capacity_(weight_capacity) {}
private:
type_subproblem
calc_subproblem(type_level level, type_size items_count,
type_weight weight_capacity) const override {
if (items_count == 0) {
return type_subproblem(0); // 0 items means 0 value for any maximum
// weight.
}
if (weight_capacity == 0) {
return type_subproblem(0); // 0 max weight means 0 weight.
}
// std::cout << " calc: i=" << items_count << ", w=" << weight_capacity <<
// std::endl;
// When items_count is 1, we want to look at the first item (index 0):
assert((items_count - 1) < items_.size());
const auto& item = items_[items_count - 1];
// If this item's weight alone is too much,
// try the previously-calculated lesser number of items,
// and don't bother trying any other alternative:
if (item.weight > weight_capacity) {
return get_subproblem(level, items_count - 1, weight_capacity);
}
// Case 1: This item is not in the optimal solution,
// so the optimal solution would be unchanged by ignoring this item (not
// including it in the optional solution)
//
// The value for same max weight with 1 less item.
// This recurses, so it really tells us the max possible value across all of
// the earlier items, for the same weight capacity.
const auto subproblem_1_less_item =
get_subproblem(level, items_count - 1, weight_capacity);
// Case 2: This item is in the optimal solution (and is the last item in
// it),
// so the optimal solution's value is equal to the solution for 1 less item,
// with less capacity,
// plus the item's value.
//
// The value for the max weight minus the current item's weight, with 1 less
// item, plus the current item's value.
auto subproblem_1_less_item_less_weight =
get_subproblem(level, items_count - 1, weight_capacity - item.weight);
subproblem_1_less_item_less_weight.value += item.value;
if (subproblem_1_less_item.value >
subproblem_1_less_item_less_weight.value) {
return subproblem_1_less_item;
} else {
subproblem_1_less_item_less_weight.solution.emplace_back(item);
return subproblem_1_less_item_less_weight;
}
}
void
get_goal_cell(type_size& items_count, type_weight& weight) const override {
// The answer is in the last-calculated cell:
items_count = items_.size();
weight = weight_capacity_;
}
const type_vec_items items_;
const type_weight weight_capacity_;
};
void
print_vec(const std::vector<Item>& vec) {
for (auto item : vec) {
std::cout << "{" << item.value << ", " << item.weight << "}, ";
}
}
int
main() {
DpKnapsack::type_vec_items items{{13, 15}, {12, 14}, {2, 13}, {10, 12},
{1, 11}, {7, 10}, {6, 9}, {5, 8}, {11, 7}, {14, 6}, {9, 5}, {4, 4}, {3, 3},
{22, 2}, {8, 1}};
const DpKnapsack::type_value weight_capacity = 45;
std::cout << "Problem: weight capacity: " << weight_capacity << std::endl
<< "with items: ";
print_vec(items);
std::cout << std::endl;
DpKnapsack dp(items, weight_capacity);
const auto result = dp.calc();
std::cout << "solution: value: " << result.value << std::endl
<< "with solution: ";
print_vec(result.solution);
std::cout << std::endl;
// Uncomment to show the sequence: dp.print_subproblem_sequence();
assert(result.value == 84);
return EXIT_SUCCESS;
}