Skip to content

Commit

Permalink
Add problem 1970: Last Day Where You Can Still Cross
Browse files Browse the repository at this point in the history
  • Loading branch information
EFanZh committed Jul 21, 2024
1 parent 357b16c commit d03cce7
Show file tree
Hide file tree
Showing 3 changed files with 291 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
158 changes: 158 additions & 0 deletions src/problem_1970_last_day_where_you_can_still_cross/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
pub mod union_find;

pub trait Solution {
fn latest_day_to_cross(row: i32, col: i32, cells: Vec<Vec<i32>>) -> i32;
}

#[cfg(test)]
mod tests {
use super::Solution;

pub fn run<S: Solution>() {
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,
);
}
}
}
132 changes: 132 additions & 0 deletions src/problem_1970_last_day_where_you_can_still_cross/union_find.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
pub struct Solution;

// ------------------------------------------------------ snip ------------------------------------------------------ //

use std::cell::Cell;
use std::{mem, ptr};

type State = (Cell<u16>, Cell<u16>);

impl Solution {
fn get_node(array: &[State], value: &State) -> u16 {
((ptr::from_ref(value) as usize - array.as_ptr() as usize) / mem::size_of::<State>()) 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<Vec<i32>>) -> 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<Vec<i32>>) -> i32 {
Self::latest_day_to_cross(row, col, cells)
}
}

#[cfg(test)]
mod tests {
#[test]
fn test_solution() {
super::super::tests::run::<super::Solution>();
}
}

0 comments on commit d03cce7

Please sign in to comment.