diff --git a/src/lib.rs b/src/lib.rs index bda607c13..c581305fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1458,6 +1458,7 @@ pub mod problem_1963_minimum_number_of_swaps_to_make_the_string_balanced; pub mod problem_1967_number_of_strings_that_appear_as_substrings_in_word; pub mod problem_1968_array_with_elements_not_equal_to_average_of_neighbors; pub mod problem_1969_minimum_non_zero_product_of_the_array_elements; +pub mod problem_1970_last_day_where_you_can_still_cross; pub mod problem_1971_find_if_path_exists_in_graph; pub mod problem_1974_minimum_time_to_type_word_using_special_typewriter; pub mod problem_1975_maximum_matrix_sum; diff --git a/src/problem_1970_last_day_where_you_can_still_cross/mod.rs b/src/problem_1970_last_day_where_you_can_still_cross/mod.rs new file mode 100644 index 000000000..d33ec9f18 --- /dev/null +++ b/src/problem_1970_last_day_where_you_can_still_cross/mod.rs @@ -0,0 +1,158 @@ +pub mod union_find; + +pub trait Solution { + fn latest_day_to_cross(row: i32, col: i32, cells: Vec>) -> i32; +} + +#[cfg(test)] +mod tests { + use super::Solution; + + pub fn run() { + let test_cases = [ + ((2, 2, &[[1, 1], [2, 1], [1, 2], [2, 2]] as &[_]), 2), + ((2, 2, &[[1, 1], [1, 2], [2, 1], [2, 2]]), 1), + ( + ( + 3, + 3, + &[[1, 2], [2, 1], [3, 3], [2, 2], [1, 1], [1, 3], [2, 3], [3, 2], [3, 1]], + ), + 3, + ), + ( + ( + 13, + 9, + &[ + [12, 6], + [3, 4], + [2, 9], + [9, 4], + [9, 2], + [6, 4], + [4, 4], + [8, 6], + [4, 9], + [5, 6], + [7, 5], + [12, 4], + [11, 8], + [3, 7], + [2, 6], + [9, 8], + [3, 5], + [13, 4], + [1, 3], + [10, 2], + [8, 9], + [6, 6], + [11, 7], + [11, 1], + [13, 9], + [12, 7], + [10, 7], + [8, 2], + [1, 8], + [7, 3], + [6, 5], + [2, 1], + [10, 6], + [4, 8], + [4, 2], + [9, 7], + [6, 2], + [3, 6], + [12, 2], + [10, 3], + [10, 5], + [9, 5], + [8, 8], + [8, 7], + [3, 2], + [13, 6], + [3, 1], + [5, 1], + [2, 7], + [8, 3], + [12, 5], + [11, 2], + [6, 3], + [1, 4], + [13, 3], + [4, 1], + [9, 9], + [7, 7], + [4, 3], + [12, 1], + [2, 2], + [7, 6], + [4, 6], + [7, 9], + [7, 2], + [3, 8], + [1, 6], + [11, 3], + [11, 4], + [5, 9], + [13, 8], + [1, 9], + [10, 1], + [9, 1], + [6, 1], + [10, 9], + [12, 9], + [11, 5], + [8, 1], + [13, 5], + [9, 6], + [13, 2], + [6, 8], + [2, 8], + [5, 3], + [3, 3], + [13, 1], + [11, 9], + [9, 3], + [2, 4], + [5, 2], + [8, 5], + [13, 7], + [12, 8], + [5, 5], + [7, 1], + [7, 4], + [2, 5], + [6, 9], + [4, 7], + [5, 8], + [1, 5], + [10, 8], + [8, 4], + [1, 1], + [3, 9], + [1, 2], + [7, 8], + [1, 7], + [6, 7], + [11, 6], + [4, 5], + [5, 7], + [2, 3], + [10, 4], + [5, 4], + [12, 3], + ], + ), + 35, + ), + ]; + + for ((row, col, cells), expected) in test_cases { + assert_eq!( + S::latest_day_to_cross(row, col, cells.iter().map(Vec::from).collect()), + expected, + ); + } + } +} diff --git a/src/problem_1970_last_day_where_you_can_still_cross/union_find.rs b/src/problem_1970_last_day_where_you_can_still_cross/union_find.rs new file mode 100644 index 000000000..fd22e949c --- /dev/null +++ b/src/problem_1970_last_day_where_you_can_still_cross/union_find.rs @@ -0,0 +1,132 @@ +pub struct Solution; + +// ------------------------------------------------------ snip ------------------------------------------------------ // + +use std::cell::Cell; +use std::{mem, ptr}; + +type State = (Cell, Cell); + +impl Solution { + fn get_node(array: &[State], value: &State) -> u16 { + ((ptr::from_ref(value) as usize - array.as_ptr() as usize) / mem::size_of::()) as _ + } + + fn find_root<'a>(union_find: &'a [State], node_state: &'a State) -> &'a State { + let parent = node_state.0.get(); + + union_find.get(usize::from(parent)).map_or(node_state, |parent_state| { + let root = Self::find_root(union_find, parent_state); + + node_state.0.set(Self::get_node(union_find, root)); + + root + }) + } + + fn union(union_find: &[State], left_state: &State, right_state: &State) { + let left_root = Self::find_root(union_find, left_state); + let right_root = Self::find_root(union_find, right_state); + + if !ptr::eq(left_root, right_root) { + let left_rank = left_root.1.get(); + let right_rank = right_root.1.get(); + + if left_rank < right_rank { + left_root.0.set(Self::get_node(union_find, right_root)); + } else { + if left_rank == right_rank { + left_root.1.set(left_rank + 1); + } + + right_root.0.set(Self::get_node(union_find, left_root)); + } + } + } + + pub fn latest_day_to_cross(row: i32, col: i32, cells: Vec>) -> i32 { + // Rotate the matrix. + + let (row, col) = (usize::from(col as u16), usize::from(row as u16)); + let stride = col + 2; + let union_find = vec![(Cell::new(u16::MAX), Cell::new(u16::MAX)); stride * (row + 2)]; + + // Fill top border. + + union_find.first().unwrap().1.set(1); + + for state in &union_find[1..stride] { + state.0.set(0); + state.1.set(0); + } + + // Fill bottom border. + + let bottom_start = union_find.len() - stride; + + union_find[bottom_start].1.set(1); + + for state in &union_find[bottom_start + 1..] { + state.0.set(bottom_start as _); + state.1.set(0); + } + + // Union neighboring cells. + + let start_state = union_find.first().unwrap(); + let end_state = union_find.last().unwrap(); + let mut day = 0; + + for cell in cells { + let [x, y]: [_; 2] = cell.try_into().ok().unwrap(); + let node = stride * usize::from(y as u16) + usize::from(x as u16); + let node_state = &union_find[node]; + + node_state.1.set(0); + + for neighbor in [ + node - stride - 1, + node - stride, + node - stride + 1, + node - 1, + node + 1, + node + stride - 1, + node + stride, + node + stride + 1, + ] { + let neighbor_state = &union_find[neighbor]; + + if neighbor_state.1.get() != u16::MAX { + Self::union(&union_find, node_state, neighbor_state); + } + } + + if ptr::eq( + Self::find_root(&union_find, start_state), + Self::find_root(&union_find, end_state), + ) { + break; + } + + day += 1; + } + + day + } +} + +// ------------------------------------------------------ snip ------------------------------------------------------ // + +impl super::Solution for Solution { + fn latest_day_to_cross(row: i32, col: i32, cells: Vec>) -> i32 { + Self::latest_day_to_cross(row, col, cells) + } +} + +#[cfg(test)] +mod tests { + #[test] + fn test_solution() { + super::super::tests::run::(); + } +}