Finish day16-part2, took a moment
This commit is contained in:
parent
25c3ca4ae2
commit
05a4d00fdb
@ -19,6 +19,15 @@ impl Valve {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct GlobalState {
|
||||||
|
nodes: HashMap<Name, Valve>,
|
||||||
|
distances: HashMap<(Name, Name), i32>,
|
||||||
|
source: Vec<Name>,
|
||||||
|
dest_a: Vec<Name>,
|
||||||
|
dest_b: Vec<Name>,
|
||||||
|
optimum: i32,
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Use command line arguments to specify the input filename.
|
// Use command line arguments to specify the input filename.
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
@ -126,162 +135,155 @@ fn main() {
|
|||||||
// Ensure each run is deterministic.
|
// Ensure each run is deterministic.
|
||||||
non_zero_nodes.sort();
|
non_zero_nodes.sort();
|
||||||
|
|
||||||
// Now check through all possible permutations of non-zero nodes using a recursive function.
|
// Create the GlobalState that is being passed through all iterations of the recursion.
|
||||||
// We assume 'AA' has zero flow_rate (it should).
|
// We assume 'AA' has zero flow_rate (it should).
|
||||||
let mut optimum: i32 = 0;
|
let mut gs = GlobalState {
|
||||||
generate_permutation(
|
nodes,
|
||||||
&nodes,
|
distances,
|
||||||
&distances,
|
source: non_zero_nodes,
|
||||||
&mut non_zero_nodes,
|
dest_a: vec![('A', 'A')],
|
||||||
&mut vec![('A', 'A')],
|
dest_b: vec![('A', 'A')],
|
||||||
&mut vec![('A', 'A')],
|
optimum: 0,
|
||||||
26,
|
};
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
&mut optimum,
|
|
||||||
);
|
|
||||||
|
|
||||||
println!("Optimal pressure release: {}", optimum);
|
// Now check through all possible permutations of non-zero nodes using a recursive function.
|
||||||
|
generate_permutation(&mut gs, 26, 0, 0, 0);
|
||||||
|
|
||||||
|
println!("Optimal pressure release: {}", gs.optimum);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_permutation(
|
fn generate_permutation(
|
||||||
nodes: &HashMap<Name, Valve>,
|
gs: &mut GlobalState,
|
||||||
distances: &HashMap<(Name, Name), i32>,
|
|
||||||
source: &mut Vec<Name>,
|
|
||||||
dest_a: &mut Vec<Name>,
|
|
||||||
dest_b: &mut Vec<Name>,
|
|
||||||
time_left: i32,
|
time_left: i32,
|
||||||
busy_a: i32,
|
busy_a: i32,
|
||||||
busy_b: i32,
|
busy_b: i32,
|
||||||
pressure: i32,
|
pressure: i32,
|
||||||
optimum: &mut i32,
|
|
||||||
) {
|
) {
|
||||||
|
// Our actors cannot be busy beyond the time limit.
|
||||||
// print!("Source: ");
|
assert!(busy_a <= time_left);
|
||||||
// print_name_list(source);
|
assert!(busy_b <= time_left);
|
||||||
// print!("A dest: ");
|
assert!(time_left >= 0);
|
||||||
// print_name_list(dest_a);
|
|
||||||
// print!("B dest: ");
|
|
||||||
// print_name_list(dest_b);
|
|
||||||
// println!("Pressure: {} | Optimum: {}\n", pressure, optimum);
|
|
||||||
|
|
||||||
// Are we done here?
|
// Are we done here?
|
||||||
// If there are only 2 units of time (or less) left we're done here.
|
// If there are only 2 units of time (or less) left we're done here.
|
||||||
// We're obviously also done if the source is empty.
|
// We're obviously also done if the source is empty.
|
||||||
if time_left <= 2 || source.is_empty() {
|
if time_left <= 2 || gs.source.is_empty() {
|
||||||
// Check if this is better and store the optimal result.
|
// Check if this is better and store the optimal result.
|
||||||
if pressure > *optimum {
|
if pressure > gs.optimum {
|
||||||
print!("A dest: ");
|
print!("A dest: ");
|
||||||
print_name_list(dest_a);
|
print_name_list(&gs.dest_a);
|
||||||
print!("B dest: ");
|
print!("B dest: ");
|
||||||
print_name_list(dest_b);
|
print_name_list(&gs.dest_b);
|
||||||
println!("New Optimum: {}\n", pressure);
|
println!("New Optimum: {}\n", pressure);
|
||||||
*optimum = pressure;
|
gs.optimum = pressure;
|
||||||
}
|
}
|
||||||
// Obviously, return early.
|
// Obviously, return early.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Differentiate between four cases:
|
|
||||||
// 1: Both elephant and us are busy. => Nothing to do, time passes.
|
// Cut criterion: If magically duplicating yourself, traveling to and opening all remaining valves doesn't
|
||||||
// 2/3: Either elephant or us are free. => Choose next destination for free
|
// yield a result better than a previous optimum, this is a waste of time.
|
||||||
|
let mut pressure_gain = 0;
|
||||||
|
for e in gs.source.iter() {
|
||||||
|
// Determine the distance between the last two nodes for both actors.
|
||||||
|
let add_dist_a = gs.distances[&(*gs.dest_a.last().unwrap(), *e)];
|
||||||
|
let add_dist_b = gs.distances[&(*gs.dest_b.last().unwrap(), *e)];
|
||||||
|
|
||||||
|
// Determine the pressure gain from both actors' positions.
|
||||||
|
let new_time_a = time_left - (1 + add_dist_a);
|
||||||
|
let add_pressure_a = new_time_a * gs.nodes[&e].flow_rate;
|
||||||
|
|
||||||
|
let new_time_b = time_left - (1 + add_dist_b);
|
||||||
|
let add_pressure_b = new_time_b * gs.nodes[&e].flow_rate;
|
||||||
|
|
||||||
|
// Add the bigger one, i.e. "clone" the actor that is closer to the node we're currently evaluating.
|
||||||
|
// Moreover, only add pressure that actually contributes to the optimum.
|
||||||
|
let gain = std::cmp::max(add_pressure_a, add_pressure_b);
|
||||||
|
if gain >= 0 {
|
||||||
|
pressure_gain += gain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the "duplicate yourself" pressure gain doesn't outperform the optimum there's no need to
|
||||||
|
// keep going.
|
||||||
|
if pressure + pressure_gain <= gs.optimum {
|
||||||
|
// println!("Cut!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// If both actors are busy, simply let time pass.
|
// If both actors are busy, simply let time pass.
|
||||||
if busy_a > 0 && busy_b > 0 {
|
if busy_a > 0 && busy_b > 0 {
|
||||||
let pass = std::cmp::min(busy_a, busy_b);
|
let pass = std::cmp::min(busy_a, busy_b);
|
||||||
// Call recursively.
|
generate_permutation(gs, time_left - pass, busy_a - pass, busy_b - pass, pressure);
|
||||||
generate_permutation(
|
|
||||||
nodes,
|
|
||||||
distances,
|
|
||||||
source,
|
|
||||||
dest_a,
|
|
||||||
dest_b,
|
|
||||||
time_left - pass,
|
|
||||||
busy_a - pass,
|
|
||||||
busy_b - pass,
|
|
||||||
pressure,
|
|
||||||
optimum,
|
|
||||||
);
|
|
||||||
} else if busy_a == 0 {
|
} else if busy_a == 0 {
|
||||||
// First actor is free to choose their next destination.
|
// First actor is free to choose their next destination.
|
||||||
|
for i in 0..gs.source.len() {
|
||||||
for i in 0..source.len() {
|
|
||||||
// Remove the current element from the vector.
|
// Remove the current element from the vector.
|
||||||
let e = source.remove(i);
|
let e = gs.source.remove(i);
|
||||||
|
|
||||||
// Determine the distance between the last two nodes.
|
// Determine the distance between the last two nodes.
|
||||||
let add_dist = distances[&(*dest_a.last().unwrap(), e)];
|
let add_dist = gs.distances[&(*gs.dest_a.last().unwrap(), e)];
|
||||||
|
|
||||||
// Put the element onto the destination.
|
// Put the element onto the destination.
|
||||||
dest_a.push(e);
|
gs.dest_a.push(e);
|
||||||
|
|
||||||
// Determine how much time will have passed until this node is ready.
|
// Determine how much time will have passed until this node is ready.
|
||||||
let new_time = time_left - (1 + add_dist);
|
let new_time = time_left - (1 + add_dist);
|
||||||
// Calculate how much pressure we save.
|
// Calculate how much pressure we save.
|
||||||
let add_pressure = new_time * nodes[&e].flow_rate;
|
let add_pressure = new_time * gs.nodes[&e].flow_rate;
|
||||||
|
|
||||||
|
// Don't even bother with this node if there is nothing to be gained.
|
||||||
|
if new_time > 0 {
|
||||||
// Call recursively, but don't let any time pass.
|
// Call recursively, but don't let any time pass.
|
||||||
// If busy_b == 0, the next recursive step will choose for the other actor.
|
// If busy_b == 0, the next recursive step will choose for the other actor.
|
||||||
// If busy_b > 0, the next recursive step will simply count down the time.
|
// If busy_b > 0, the next recursive step will simply count down the time.
|
||||||
|
generate_permutation(gs, time_left, 1 + add_dist, busy_b, pressure + add_pressure);
|
||||||
generate_permutation(
|
|
||||||
nodes,
|
|
||||||
distances,
|
|
||||||
source,
|
|
||||||
dest_a,
|
|
||||||
dest_b,
|
|
||||||
time_left,
|
|
||||||
1 + add_dist,
|
|
||||||
busy_b,
|
|
||||||
pressure + add_pressure,
|
|
||||||
optimum,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Remove the element from the destination.
|
|
||||||
dest_a.pop();
|
|
||||||
// And reinsert the element back into the vector, at the same precise location.
|
|
||||||
source.insert(i, e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove the element from the destination.
|
||||||
|
gs.dest_a.pop();
|
||||||
|
// And reinsert the element back into the vector, at the same precise location.
|
||||||
|
gs.source.insert(i, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// OK, time to check one more thing: Doing nothing.
|
||||||
|
// There is a possibility that the optimal strat for this actor is to twiddle their thumbs and
|
||||||
|
// let the other actor handle all the remaining sources.
|
||||||
|
generate_permutation(gs, time_left, time_left, busy_b, pressure);
|
||||||
} else if busy_b == 0 {
|
} else if busy_b == 0 {
|
||||||
// Second actor is free to choose their next destination.
|
// Second actor is free to choose their next destination.
|
||||||
|
|
||||||
// Iterate through all elements remaining in the source.
|
// Iterate through all elements remaining in the source.
|
||||||
for i in 0..source.len() {
|
for i in 0..gs.source.len() {
|
||||||
// Remove the current element from the vector.
|
// Remove the current element from the vector.
|
||||||
let e = source.remove(i);
|
let e = gs.source.remove(i);
|
||||||
|
|
||||||
// Determine the distance between the last two nodes.
|
// Determine the distance between the last two nodes.
|
||||||
let add_dist = distances[&(*dest_b.last().unwrap(), e)];
|
let add_dist = gs.distances[&(*gs.dest_b.last().unwrap(), e)];
|
||||||
|
|
||||||
// Put the element onto the destination.
|
// Put the element onto the destination.
|
||||||
dest_b.push(e);
|
gs.dest_b.push(e);
|
||||||
|
|
||||||
// Determine how much time will have passed until this node is ready.
|
// Determine how much time will have passed until this node is ready.
|
||||||
let new_time = time_left - (1 + add_dist);
|
let new_time = time_left - (1 + add_dist);
|
||||||
// Calculate how much pressure we save.
|
// Calculate how much pressure we save.
|
||||||
let add_pressure = new_time * nodes[&e].flow_rate;
|
let add_pressure = new_time * gs.nodes[&e].flow_rate;
|
||||||
|
|
||||||
|
// Don't even bother with this node if there is nothing to be gained.
|
||||||
|
if new_time > 0 {
|
||||||
// Call recursively, but don't let any time pass.
|
// Call recursively, but don't let any time pass.
|
||||||
generate_permutation(
|
generate_permutation(gs, time_left, busy_a, 1 + add_dist, pressure + add_pressure);
|
||||||
nodes,
|
|
||||||
distances,
|
|
||||||
source,
|
|
||||||
dest_a,
|
|
||||||
dest_b,
|
|
||||||
time_left,
|
|
||||||
busy_a,
|
|
||||||
1 + add_dist,
|
|
||||||
pressure + add_pressure,
|
|
||||||
optimum,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Remove the element from the destination.
|
|
||||||
dest_b.pop();
|
|
||||||
// And reinsert the element back into the vector, at the same precise location.
|
|
||||||
source.insert(i, e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove the element from the destination.
|
||||||
|
gs.dest_b.pop();
|
||||||
|
// And reinsert the element back into the vector, at the same precise location.
|
||||||
|
gs.source.insert(i, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// OK, time to check one more thing: Doing nothing.
|
||||||
|
// There is a possibility that the optimal strat for this actor is to twiddle their thumbs and
|
||||||
|
// let the other actor handle all the remaining sources.
|
||||||
|
generate_permutation(gs, time_left, busy_a, time_left, pressure);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user