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
//! # It Hangs in the Balance
//!
//! To simplify things assumes that the remaining items after the first best combination is found
//! can be split evenly.
//!
//! Sorts the weights in ascending order, then tries combinations of increasing size until a
//! match in found. This will be the answer since the package count is the smallest and the
//! quantum entaglement will also be the lowest.
use crate::util::parse::*;
pub fn parse(input: &str) -> Vec<u64> {
let mut packages: Vec<_> = input.iter_unsigned().collect();
packages.sort_unstable();
packages
}
pub fn part1(input: &[u64]) -> u64 {
let sum: u64 = input.iter().sum();
let target = sum / 3;
(1..input.len()).find_map(|size| combinations(input, target, size)).unwrap()
}
pub fn part2(input: &[u64]) -> u64 {
let sum: u64 = input.iter().sum();
let target = sum / 4;
(1..input.len()).find_map(|size| combinations(input, target, size)).unwrap()
}
/// Check all combinations of `size` items returning `None` if no valid solution is found.
fn combinations(packages: &[u64], target: u64, size: usize) -> Option<u64> {
// Mantain `size` indices, initially set to 0, 1, 2...
let mut indices: Vec<_> = (0..size).collect();
// Initial weight for first `size` items.
let mut weight: u64 = packages.iter().take(size).sum();
loop {
// Check for success
if weight == target {
let product = indices.iter().map(|&i| packages[i]).product();
return Some(product);
}
// Try to advance the last index. If the last index is at the end, then try to advance
// the previous index until we reach the root.
let mut depth = size - 1;
while indices[depth] == packages.len() - size + depth {
if depth == 0 {
return None;
}
depth -= 1;
}
// Update the first index that is not at the end.
let from = indices[depth];
let to = indices[depth] + 1;
indices[depth] = to;
weight = weight - packages[from] + packages[to];
depth += 1;
// "Wrap" following indices to 1 more than the previous.
while depth < size {
let from = indices[depth];
let to = indices[depth - 1] + 1;
indices[depth] = to;
weight = weight - packages[from] + packages[to];
depth += 1;
}
}
}