2015: d15: ex2: add solution
This commit is contained in:
parent
e8ef80c528
commit
151ab3e24f
1 changed files with 85 additions and 0 deletions
85
2015/d15/ex2/ex2.py
Executable file
85
2015/d15/ex2/ex2.py
Executable file
|
|
@ -0,0 +1,85 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
from collections.abc import Iterator
|
||||
from typing import Literal, NamedTuple, cast
|
||||
|
||||
|
||||
class Properties(NamedTuple):
|
||||
capacity: int
|
||||
durability: int
|
||||
flavor: int
|
||||
texture: int
|
||||
calories: int
|
||||
|
||||
@classmethod
|
||||
def from_str(cls, input: str) -> "Properties":
|
||||
properties = map(str.split, input.split(", "))
|
||||
return cls(*(int(prop[-1]) for prop in properties))
|
||||
|
||||
|
||||
PropertyName = Literal["capacity", "durability", "flavor", "texture", "calories"]
|
||||
|
||||
|
||||
def solve(input: str) -> int:
|
||||
def parse_line(input: str) -> tuple[str, Properties]:
|
||||
ingredient, properties = input.split(": ")
|
||||
return ingredient, Properties.from_str(properties)
|
||||
|
||||
def parse(input: str) -> dict[str, Properties]:
|
||||
return {name: prop for name, prop in map(parse_line, input.splitlines())}
|
||||
|
||||
def sum_properties(
|
||||
ingredients: dict[str, Properties],
|
||||
amounts: dict[str, int],
|
||||
prop: PropertyName,
|
||||
) -> int:
|
||||
return sum(
|
||||
getattr(ingredients[name], prop) * amounts[name]
|
||||
for name in ingredients.keys()
|
||||
)
|
||||
|
||||
def score(ingredients: dict[str, Properties], amounts: dict[str, int]) -> int:
|
||||
assert ingredients.keys() == amounts.keys() # Sanity check
|
||||
assert sum(amounts.values()) == 100 # Sanity check
|
||||
res = 1
|
||||
for prop in ("capacity", "durability", "flavor", "texture"):
|
||||
res *= max(
|
||||
0,
|
||||
sum_properties(ingredients, amounts, cast(PropertyName, prop)),
|
||||
)
|
||||
return res
|
||||
|
||||
def permute_amounts(ingredients: dict[str, Properties]) -> Iterator[dict[str, int]]:
|
||||
def helper(amounts: dict[str, int]) -> Iterator[dict[str, int]]:
|
||||
remaining = 100 - sum(amounts.values())
|
||||
assert remaining >= 0 # Sanity check
|
||||
assert ingredients # Sanity check
|
||||
|
||||
current = next(iter(n for n in ingredients.keys() if n not in amounts))
|
||||
if (len(amounts) + 1) == len(ingredients):
|
||||
yield amounts | {current: remaining}
|
||||
else:
|
||||
for i in range(remaining):
|
||||
yield from helper(amounts | {current: i})
|
||||
|
||||
yield from helper({})
|
||||
|
||||
def maximize_score(ingredient: dict[str, Properties]) -> int:
|
||||
return max(
|
||||
score(ingredient, amounts)
|
||||
for amounts in permute_amounts(ingredients)
|
||||
if sum_properties(ingredient, amounts, "calories") == 500
|
||||
)
|
||||
|
||||
ingredients = parse(input)
|
||||
return maximize_score(ingredients)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
input = sys.stdin.read()
|
||||
print(solve(input))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue