135 lines
5.5 KiB
Plaintext
135 lines
5.5 KiB
Plaintext
#![feature(iter_arith)]
|
|
use std::cmp::max;
|
|
use std::vec::Vec;
|
|
|
|
|
|
// This struct is used to store our items that we want in our knap-sack.
|
|
#[derive(Clone, Debug)]
|
|
struct Item<'a> {
|
|
name: &'a str,
|
|
weight: usize,
|
|
value: usize
|
|
}
|
|
|
|
|
|
// This is a bottom-up dynamic programming solution to the 0-1 knap-sack problem.
|
|
// maximize value
|
|
// subject to weights <= max_weight
|
|
fn knapsack01_dyn<'a>(items: &[Item<'a>], max_weight: usize) -> Vec<Item<'a>> {
|
|
// Imagine we wrote a recursive function(item, max_weight) that returns a
|
|
// usize corresponding to the maximum cumulative value by considering a
|
|
// subset of items such that the combined weight <= max_weight.
|
|
//
|
|
// fn best_value(item: usize, max_weight: usize) -> usize {
|
|
// if item == 0 {
|
|
// return 0;
|
|
// }
|
|
// if items[item - 1].weight > max_weight {
|
|
// return best_value(item - 1, max_weight);
|
|
// }
|
|
// return max(best_value(item - 1, max_weight),
|
|
// best_value(item - 1, max_weight - items[item - 1].weight)
|
|
// + items[item - 1].value);
|
|
// }
|
|
// }
|
|
//
|
|
// best_value(n_items, max_weight) is equal to the maximum value that
|
|
// we can add to the bag.
|
|
//
|
|
// The problem with using this function is that it performs redundant
|
|
// calculations.
|
|
//
|
|
// The dynamic programming solution is to precompute all of the values
|
|
// we need and put them into a 2D array.
|
|
//
|
|
// In a similar vein, the top-down solution would be to memoize the
|
|
// function then compute the results on demand.
|
|
|
|
let mut best_value = vec![vec![0usize; max_weight + 1]; items.len() + 1];
|
|
|
|
// Loop over the items.
|
|
for (i, it) in items.iter().enumerate() {
|
|
// Loop over the weights.
|
|
for w in 1 .. max_weight + 1 {
|
|
best_value[i + 1][w] =
|
|
// do we have room in our knapsack?
|
|
if it.weight > w {
|
|
// if we don't, then we'll say that the value doesn't change
|
|
// when considering this item
|
|
best_value[i][w].clone()
|
|
} else {
|
|
// If we do, then we have to see if the value we gain by adding
|
|
// the item, given the weight, is better than not adding the item.
|
|
max(best_value[i][w].clone(), best_value[i][w - it.weight] + it.value)
|
|
}
|
|
}
|
|
}
|
|
|
|
// A possibly over-allocated dynamically sized vector to push results to.
|
|
let mut result = Vec::with_capacity(items.len());
|
|
|
|
// Variable representing the weight left in the bag
|
|
let mut left_weight = max_weight.clone();
|
|
|
|
// We built up the solution space through a forward pass over the data,
|
|
// now we have to traverse backwards to get the solution.
|
|
for (i, it) in items.iter().enumerate().rev() {
|
|
// We can check if an item should be added to the knap-sack by
|
|
// comparing best_value with and without this item. If best_value
|
|
// added this item then so should we.
|
|
if best_value[i + 1][left_weight] != best_value[i][left_weight] {
|
|
result.push(it.clone());
|
|
// We remove the weight of the object from the remaining weight
|
|
// we can add to the bag.
|
|
left_weight -= it.weight;
|
|
}
|
|
}
|
|
|
|
result
|
|
}
|
|
|
|
|
|
fn main () {
|
|
const MAX_WEIGHT: usize = 400;
|
|
|
|
// Static immutable allocation of our items.
|
|
static ITEMS: &'static [Item<'static>] = &[
|
|
// Too much repetition of field names here!
|
|
Item { name: "map", weight: 9, value: 150 },
|
|
Item { name: "compass", weight: 13, value: 35 },
|
|
Item { name: "water", weight: 153, value: 200 },
|
|
Item { name: "sandwich", weight: 50, value: 160 },
|
|
Item { name: "glucose", weight: 15, value: 60 },
|
|
Item { name: "tin", weight: 68, value: 45 },
|
|
Item { name: "banana", weight: 27, value: 60 },
|
|
Item { name: "apple", weight: 39, value: 40 },
|
|
Item { name: "cheese", weight: 23, value: 30 },
|
|
Item { name: "beer", weight: 52, value: 10 },
|
|
Item { name: "suntancream", weight: 11, value: 70 },
|
|
Item { name: "camera", weight: 32, value: 30 },
|
|
Item { name: "T-shirt", weight: 24, value: 15 },
|
|
Item { name: "trousers", weight: 48, value: 10 },
|
|
Item { name: "umbrella", weight: 73, value: 40 },
|
|
Item { name: "waterproof trousers", weight: 42, value: 70 },
|
|
Item { name: "waterproof overclothes", weight: 43, value: 75 },
|
|
Item { name: "note-case", weight: 22, value: 80 },
|
|
Item { name: "sunglasses", weight: 7, value: 20 },
|
|
Item { name: "towel", weight: 18, value: 12 },
|
|
Item { name: "socks", weight: 4, value: 50 },
|
|
Item { name: "book", weight: 30, value: 10 }
|
|
];
|
|
|
|
let items = knapsack01_dyn(ITEMS, MAX_WEIGHT);
|
|
|
|
// We reverse the order because we solved the problem backward.
|
|
for it in items.iter().rev() {
|
|
println!("{:?}", it);
|
|
}
|
|
|
|
let tot_weight: usize = items.iter().map(|w| w.weight).sum();
|
|
println!("Total weight: {}", tot_weight);
|
|
|
|
let tot_value: usize = items.iter().map(|w| w.value).sum();
|
|
println!("Total value: {}", tot_value);
|
|
}
|