Maximum Value of the Loot
Introduction
A thief breaks into a spice shop and finds spices with varying values per pound. She needs to be able to maximize the amount she steals by stuffing spices into her backpack.
Description | |
First Input | \(n\), the number of compounds and \(W\), the capacity of the backpack. |
Remaining Inputs | \(n\) lines of price-per-pound and weight of each compound |
Output | Maximum price of spices stuffed into the backpack. |
Constraints | \(1 \le n \le 10^3\), \(0 \le W \le 2 \cdot 10^6\), \(0 \le p_i \le 2 \cdot 10^6\), \(0 \le w_i \le 2 \cdot 10^6\) for \(1 \le i \le n\) |
Although the inputs will always be integers, the outputs might be real numbers. To match the grader output at least four digits to the right of the decimal point.
Samples
Sample One
Input:
3 50 60 20 100 50 120 30
Output:
180.0000
The output tells us that the thief's maximum haul is worth $180, which if you look at the inputs means taking 20 pounds of the first spice (worth $60) and 30 pounds of the last spice.
Sample Two
1 10 500 30
Output.
166.6667
The input tells us that the thief can only carry 10 pounds of the only available spice, so her haul is \(\frac{500}{3}\approx 166.6667\).
Implementation
Because this takes a greedy approach, it will have a \(O(n)\) run-time. Since I'm sorting the values first there's actually a \(O(\log n) + O(n)\), but especially since I'm using the built-in python generators, the sort is negligible compared to the main loop.
# this package
from algorithmic_toolbox.helpers import assert_close
def maximize_loot(capacity, weights, values):
"""Figure out the maximum value the thief can haul off
Args:
capacity (int): number of pounds backpack can hold
weights (list): how many pounds of each item there is
values (list): how much each item is worth per pound
Raises:
AssertionError: weights and values are different lengths
Returns:
float: max-value the backpack can hold
"""
weight_count = len(weights)
assert weight_count == len(values), \
"Weights and Values not same shape: weights={} values={}".format(
weight_count, len(values))
values_per_pound = ((values[index]/weights[index], index)
for index in range(weight_count))
# we have to reverse-sort it (otherwise sorting puts the smallest
# number first)
per_poundage = sorted(values_per_pound, reverse=True)
# loot is the value of what we've taken so far
loot = 0
# precondition: per_poundage is the value-per-pound in descending
# order for each item along with the index of the original weight/value
for value, index in per_poundage:
# invariant: value is the largest price-per-pound available
if capacity < weights[index]:
# we don't have enough strength to take all of this item
# so just take as much as we can and quit
loot += value * capacity
break
# otherwise take all of this item
loot += values[index]
# reducing our capacity by its total weight
capacity -= weights[index]
if capacity == 0:
# we're out of capacity, quit
break
return loot
Test One
n = 3
capacity = 50
prices = [60, 100, 120]
weights = [20, 50, 30]
expected = 180.0000
actual = maximize_loot(capacity, weights, prices)
assert_close(expected, actual, "Test One")
Test Two
capacity = 10
prices = [500]
weights = [30]
expected = 166.6667
actual = maximize_loot(capacity, weights, prices)
assert_close(expected, actual, "Test Two")
Grader Output
Good job! (Max time used: 0.03/5.00, max memory used: 9752576/671088640.)