Dazbo's Advent of Code solutions, written in Python
Day 17: No Such Thing as Too Much
List comprehensionCombinations
We have 150L of eggnog, and we need to distribute this eggnog over a bunch of containers. The volumes of the containers (in L) are our puzzle input. E.g.
20
15
10
5
5
Filling all containers entirely, how many different combinations of containers can exactly fit all 150 liters of eggnog?
This is quite an easy problem. I used a fairly similar solution to Day 15.
First, we read in the numbers, and convert them to a single list of int
values:
with open(INPUT_FILE, mode="rt") as f:
containers = [int(x) for x in f.read().splitlines()]
Then:
combinations
of containers there are, using that number of containers. E.g. using the example data above:
20, 15, 10, 5, 5
(20, 15), (20, 10), (20, 5), (20, 5), (15, 10), (15, 5)...
, etc.The code looks like this:
all_valid_combos = [] # all the container combos that add up to TARGET
# Try any single container, then any two containers, then any three, etc
for num_containers in range(1, len(containers)+1):
valid_combos = [combo for combo in list(combinations(containers, num_containers))
if sum(combo) == TARGET]
all_valid_combos.extend(valid_combos)
# part 1
print(f"Part 1: Valid combos that contain {TARGET}L={len(all_valid_combos)}")
Easy!
Find the minimum number of containers that can exactly fit all 150 liters of eggnog. How many different ways can you fill that number of containers and still hold exactly 150 litres?
Well, this is very easy! We just need to do exactly the same as Part 1, but stop once we’ve got our first valid combinations. Why? Because at that point, we’ve already got the combinations using hte fewest number of containers!
# part 2
all_valid_combos = [] # all the container combos that add up to TARGET
for num_containers in range(1, len(containers)+1):
valid_combos = [combo for combo in list(combinations(containers, num_containers))
if sum(combo) == TARGET]
all_valid_combos.extend(valid_combos)
if valid_combos:
break
print(f"Part 2: Combinations with minimum number of containers={len(all_valid_combos)}")
Here’s the complete code:
from pathlib import Path
from itertools import combinations
import time
SCRIPT_DIR = Path(__file__).parent
# INPUT_FILE = Path(SCRIPT_DIR, "input/sample_input.txt")
INPUT_FILE = Path(SCRIPT_DIR, "input/input.txt")
TARGET = 150
def main():
with open(INPUT_FILE, mode="rt") as f:
containers = [int(x) for x in f.read().splitlines()]
all_valid_combos = [] # all the container combos that add up to TARGET
# Try any single container, then any two containers, then any three, etc
for num_containers in range(1, len(containers)+1):
valid_combos = [combo for combo in list(combinations(containers, num_containers))
if sum(combo) == TARGET]
all_valid_combos.extend(valid_combos)
# part 1
print(f"Part 1: Valid combos that contain {TARGET}L={len(all_valid_combos)}")
# part 2
all_valid_combos = [] # all the container combos that add up to TARGET
for num_containers in range(1, len(containers)+1):
valid_combos = [combo for combo in list(combinations(containers, num_containers))
if sum(combo) == TARGET]
all_valid_combos.extend(valid_combos)
if valid_combos:
break
print(f"Part 2: Combinations with minimum number of containers={len(all_valid_combos)}")
if __name__ == "__main__":
t1 = time.perf_counter()
main()
t2 = time.perf_counter()
print(f"Execution time: {t2 - t1:0.4f} seconds")
And here’s the output:
Part 1: Valid combos that contain 150L=4372
Part 2: Combinations with minimum number of containers=4
Execution time: 0.1281 seconds