|
1 |
| -/// # Egg Dropping Puzzle |
| 1 | +//! This module contains the `egg_drop` function, which determines the minimum number of egg droppings |
| 2 | +//! required to find the highest floor from which an egg can be dropped without breaking. It also includes |
| 3 | +//! tests for the function using various test cases, including edge cases. |
2 | 4 |
|
3 |
| -/// `egg_drop(eggs, floors)` returns the least number of egg droppings |
4 |
| -/// required to determine the highest floor from which an egg will not |
5 |
| -/// break upon dropping |
| 5 | +/// Returns the least number of egg droppings required to determine the highest floor from which an egg will not break upon dropping. |
6 | 6 | ///
|
7 |
| -/// Assumptions: n > 0 |
8 |
| -pub fn egg_drop(eggs: u32, floors: u32) -> u32 { |
9 |
| - assert!(eggs > 0); |
10 |
| - |
11 |
| - // Explicity handle edge cases (optional) |
12 |
| - if eggs == 1 || floors == 0 || floors == 1 { |
13 |
| - return floors; |
14 |
| - } |
15 |
| - |
16 |
| - let eggs_index = eggs as usize; |
17 |
| - let floors_index = floors as usize; |
18 |
| - |
19 |
| - // Store solutions to subproblems in 2D Vec, |
20 |
| - // where egg_drops[i][j] represents the solution to the egg dropping |
21 |
| - // problem with i eggs and j floors |
22 |
| - let mut egg_drops: Vec<Vec<u32>> = vec![vec![0; floors_index + 1]; eggs_index + 1]; |
23 |
| - |
24 |
| - // Assign solutions for egg_drop(n, 0) = 0, egg_drop(n, 1) = 1 |
25 |
| - for egg_drop in egg_drops.iter_mut().skip(1) { |
26 |
| - egg_drop[0] = 0; |
27 |
| - egg_drop[1] = 1; |
28 |
| - } |
29 |
| - |
30 |
| - // Assign solutions to egg_drop(1, k) = k |
31 |
| - for j in 1..=floors_index { |
32 |
| - egg_drops[1][j] = j as u32; |
| 7 | +/// # Arguments |
| 8 | +/// |
| 9 | +/// * `eggs` - The number of eggs available. |
| 10 | +/// * `floors` - The number of floors in the building. |
| 11 | +/// |
| 12 | +/// # Returns |
| 13 | +/// |
| 14 | +/// * `Some(usize)` - The minimum number of drops required if the number of eggs is greater than 0. |
| 15 | +/// * `None` - If the number of eggs is 0. |
| 16 | +pub fn egg_drop(eggs: usize, floors: usize) -> Option<usize> { |
| 17 | + if eggs == 0 { |
| 18 | + return None; |
33 | 19 | }
|
34 | 20 |
|
35 |
| - // Complete solutions vector using optimal substructure property |
36 |
| - for i in 2..=eggs_index { |
37 |
| - for j in 2..=floors_index { |
38 |
| - egg_drops[i][j] = u32::MAX; |
39 |
| - |
40 |
| - for k in 1..=j { |
41 |
| - let res = 1 + std::cmp::max(egg_drops[i - 1][k - 1], egg_drops[i][j - k]); |
42 |
| - |
43 |
| - if res < egg_drops[i][j] { |
44 |
| - egg_drops[i][j] = res; |
45 |
| - } |
46 |
| - } |
47 |
| - } |
| 21 | + if eggs == 1 || floors == 0 || floors == 1 { |
| 22 | + return Some(floors); |
48 | 23 | }
|
49 | 24 |
|
50 |
| - egg_drops[eggs_index][floors_index] |
| 25 | + // Create a 2D vector to store solutions to subproblems |
| 26 | + let mut egg_drops: Vec<Vec<usize>> = vec![vec![0; floors + 1]; eggs + 1]; |
| 27 | + |
| 28 | + // Base cases: 0 floors -> 0 drops, 1 floor -> 1 drop |
| 29 | + (1..=eggs).for_each(|i| { |
| 30 | + egg_drops[i][1] = 1; |
| 31 | + }); |
| 32 | + |
| 33 | + // Base case: 1 egg -> k drops for k floors |
| 34 | + (1..=floors).for_each(|j| { |
| 35 | + egg_drops[1][j] = j; |
| 36 | + }); |
| 37 | + |
| 38 | + // Fill the table using the optimal substructure property |
| 39 | + (2..=eggs).for_each(|i| { |
| 40 | + (2..=floors).for_each(|j| { |
| 41 | + egg_drops[i][j] = (1..=j) |
| 42 | + .map(|k| 1 + std::cmp::max(egg_drops[i - 1][k - 1], egg_drops[i][j - k])) |
| 43 | + .min() |
| 44 | + .unwrap(); |
| 45 | + }); |
| 46 | + }); |
| 47 | + |
| 48 | + Some(egg_drops[eggs][floors]) |
51 | 49 | }
|
52 | 50 |
|
53 | 51 | #[cfg(test)]
|
54 | 52 | mod tests {
|
55 |
| - use super::egg_drop; |
56 |
| - |
57 |
| - #[test] |
58 |
| - fn zero_floors() { |
59 |
| - assert_eq!(egg_drop(5, 0), 0); |
60 |
| - } |
61 |
| - |
62 |
| - #[test] |
63 |
| - fn one_egg() { |
64 |
| - assert_eq!(egg_drop(1, 8), 8); |
65 |
| - } |
66 |
| - |
67 |
| - #[test] |
68 |
| - fn eggs2_floors2() { |
69 |
| - assert_eq!(egg_drop(2, 2), 2); |
70 |
| - } |
71 |
| - |
72 |
| - #[test] |
73 |
| - fn eggs3_floors5() { |
74 |
| - assert_eq!(egg_drop(3, 5), 3); |
75 |
| - } |
76 |
| - |
77 |
| - #[test] |
78 |
| - fn eggs2_floors10() { |
79 |
| - assert_eq!(egg_drop(2, 10), 4); |
80 |
| - } |
81 |
| - |
82 |
| - #[test] |
83 |
| - fn eggs2_floors36() { |
84 |
| - assert_eq!(egg_drop(2, 36), 8); |
| 53 | + use super::*; |
| 54 | + |
| 55 | + macro_rules! egg_drop_tests { |
| 56 | + ($($name:ident: $test_cases:expr,)*) => { |
| 57 | + $( |
| 58 | + #[test] |
| 59 | + fn $name() { |
| 60 | + let (eggs, floors, expected) = $test_cases; |
| 61 | + assert_eq!(egg_drop(eggs, floors), expected); |
| 62 | + } |
| 63 | + )* |
| 64 | + } |
85 | 65 | }
|
86 | 66 |
|
87 |
| - #[test] |
88 |
| - fn large_floors() { |
89 |
| - assert_eq!(egg_drop(2, 100), 14); |
| 67 | + egg_drop_tests! { |
| 68 | + test_no_floors: (5, 0, Some(0)), |
| 69 | + test_one_egg_multiple_floors: (1, 8, Some(8)), |
| 70 | + test_multiple_eggs_one_floor: (5, 1, Some(1)), |
| 71 | + test_two_eggs_two_floors: (2, 2, Some(2)), |
| 72 | + test_three_eggs_five_floors: (3, 5, Some(3)), |
| 73 | + test_two_eggs_ten_floors: (2, 10, Some(4)), |
| 74 | + test_two_eggs_thirty_six_floors: (2, 36, Some(8)), |
| 75 | + test_many_eggs_one_floor: (100, 1, Some(1)), |
| 76 | + test_many_eggs_few_floors: (100, 5, Some(3)), |
| 77 | + test_few_eggs_many_floors: (2, 1000, Some(45)), |
| 78 | + test_zero_eggs: (0, 10, None::<usize>), |
| 79 | + test_no_eggs_no_floors: (0, 0, None::<usize>), |
| 80 | + test_one_egg_no_floors: (1, 0, Some(0)), |
| 81 | + test_one_egg_one_floor: (1, 1, Some(1)), |
| 82 | + test_maximum_floors_one_egg: (1, usize::MAX, Some(usize::MAX)), |
90 | 83 | }
|
91 | 84 | }
|
0 commit comments