|
| 1 | +# 1912. Design Movie Rental System |
| 2 | +You have a movie renting company consisting of `n` shops. You want to implement a renting system that supports searching for, booking, and returning movies. The system should also support generating a report of the currently rented movies. |
| 3 | + |
| 4 | +Each movie is given as a 2D integer array `entries` where <code>entries[i] = [shop<sub>i</sub>, movie<sub>i</sub>, price<sub>i</sub>]</code> indicates that there is a copy of movie <code>movie<sub>i</sub></code> at shop <code>shop<sub>i</sub></code> with a rental price of <code>price<sub>i</sub></code>. Each shop carries **at most one** copy of a movie <code>movie<sub>i</sub></code>. |
| 5 | + |
| 6 | +The system should support the following functions: |
| 7 | + |
| 8 | +* **Search:** Finds the **cheapest 5 shops** that have an **unrented copy** of a given movie. The shops should be sorted by **price** in ascending order, and in case of a tie, the one with the **smaller** <code>shop<sub>i</sub></code> should appear first. If there are less than 5 matching shops, then all of them should be returned. If no shop has an unrented copy, then an empty list should be returned. |
| 9 | +* **Rent:** Rents an **unrented copy** of a given movie from a given shop. |
| 10 | +* **Drop:** Drops off a **previously rented copy** of a given movie at a given shop. |
| 11 | +* **Report:** Returns the **cheapest 5 rented movies** (possibly of the same movie ID) as a 2D list `res` where <code>res[j] = [shop<sub>j</sub>, movie<sub>j</sub>]</code> describes that the <code>j<sup>th</sup></code> cheapest rented movie <code>movie<sub>j</sub></code> was rented from the shop <code>shop<sub>j</sub></code>. The movies in `res` should be sorted by **price** in ascending order, and in case of a tie, the one with the **smaller** <code>shop<sub>j</sub></code> should appear first, and if there is still tie, the one with the **smaller** <code>movie<sub>j</sub></code> should appear first. If there are fewer than 5 rented movies, then all of them should be returned. If no movies are currently being rented, then an empty list should be returned. |
| 12 | + |
| 13 | +Implement the `MovieRentingSystem` class: |
| 14 | + |
| 15 | +* `MovieRentingSystem(int n, int[][] entries)` Initializes the `MovieRentingSystem` object with `n` shops and the movies in `entries`. |
| 16 | +* `List<Integer> search(int movie)` Returns a list of shops that have an **unrented copy** of the given `movie` as described above. |
| 17 | +* `void rent(int shop, int movie)` Rents the given `movie` from the given `shop`. |
| 18 | +* `void drop(int shop, int movie)` Drops off a previously rented `movie` at the given `shop`. |
| 19 | +* `List<List<Integer>> report()` Returns a list of cheapest **rented** movies as described above. |
| 20 | + |
| 21 | +**Note:** The test cases will be generated such that `rent` will only be called if the shop has an **unrented** copy of the movie, and `drop` will only be called if the shop had **previously rented** out the movie. |
| 22 | + |
| 23 | +#### Example 1: |
| 24 | +<pre> |
| 25 | +<strong>Input:</strong> |
| 26 | +["MovieRentingSystem", "search", "rent", "rent", "report", "drop", "search"] |
| 27 | +[[3, [[0, 1, 5], [0, 2, 6], [0, 3, 7], [1, 1, 4], [1, 2, 7], [2, 1, 5]]], [1], [0, 1], [1, 2], [], [1, 2], [2]] |
| 28 | +<strong>Output:</strong> |
| 29 | +[null, [1, 0, 2], null, null, [[0, 1], [1, 2]], null, [0, 1]] |
| 30 | +<strong>Explanation:</strong> |
| 31 | +MovieRentingSystem movieRentingSystem = new MovieRentingSystem(3, [[0, 1, 5], [0, 2, 6], [0, 3, 7], [1, 1, 4], [1, 2, 7], [2, 1, 5]]); |
| 32 | +movieRentingSystem.search(1); // return [1, 0, 2], Movies of ID 1 are unrented at shops 1, 0, and 2. Shop 1 is cheapest; shop 0 and 2 are the same price, so order by shop number. |
| 33 | +movieRentingSystem.rent(0, 1); // Rent movie 1 from shop 0. Unrented movies at shop 0 are now [2,3]. |
| 34 | +movieRentingSystem.rent(1, 2); // Rent movie 2 from shop 1. Unrented movies at shop 1 are now [1]. |
| 35 | +movieRentingSystem.report(); // return [[0, 1], [1, 2]]. Movie 1 from shop 0 is cheapest, followed by movie 2 from shop 1. |
| 36 | +movieRentingSystem.drop(1, 2); // Drop off movie 2 at shop 1. Unrented movies at shop 1 are now [1,2]. |
| 37 | +movieRentingSystem.search(2); // return [0, 1]. Movies of ID 2 are unrented at shops 0 and 1. Shop 0 is cheapest, followed by shop 1. |
| 38 | +</pre> |
| 39 | + |
| 40 | +#### Constraints: |
| 41 | +* <code>1 <= n <= 3 * 10<sup>5</sup></code> |
| 42 | +* <code>1 <= entries.length <= 10<sup>5</sup></code> |
| 43 | +* <code>0 <= shop<sub>i</sub> < n</code> |
| 44 | +* <code>1 <= movie<sub>i</sub>, price<sub>i</sub> <= 10<sup>4</sup></code> |
| 45 | +* Each shop carries **at most one** copy of a movie <code>movie<sub>i</sub></code>. |
| 46 | +* At most <code>10<sup>5</sup></code> calls **in total** will be made to `search`, `rent`, `drop` and `report`. |
| 47 | + |
| 48 | +## Solutions (Rust) |
| 49 | + |
| 50 | +### 1. Solution |
| 51 | +```Rust |
| 52 | +use std::cmp::Reverse; |
| 53 | +use std::collections::BinaryHeap; |
| 54 | +use std::collections::HashMap; |
| 55 | + |
| 56 | +struct MovieRentingSystem { |
| 57 | + rented: HashMap<(i32, i32), (bool, i32)>, |
| 58 | + search_heaps: HashMap<i32, BinaryHeap<Reverse<(i32, i32)>>>, |
| 59 | + report_heap: BinaryHeap<Reverse<(i32, i32, i32)>>, |
| 60 | +} |
| 61 | + |
| 62 | +/** |
| 63 | + * `&self` means the method takes an immutable reference. |
| 64 | + * If you need a mutable reference, change it to `&mut self` instead. |
| 65 | + */ |
| 66 | +impl MovieRentingSystem { |
| 67 | + fn new(n: i32, entries: Vec<Vec<i32>>) -> Self { |
| 68 | + let mut rented = HashMap::new(); |
| 69 | + let mut search_heaps = HashMap::new(); |
| 70 | + |
| 71 | + for i in 0..entries.len() { |
| 72 | + let (shop, movie, price) = (entries[i][0], entries[i][1], entries[i][2]); |
| 73 | + rented.insert((shop, movie), (false, price)); |
| 74 | + search_heaps |
| 75 | + .entry(movie) |
| 76 | + .or_insert(BinaryHeap::new()) |
| 77 | + .push(Reverse((price, shop))); |
| 78 | + } |
| 79 | + |
| 80 | + Self { |
| 81 | + rented: rented, |
| 82 | + search_heaps: search_heaps, |
| 83 | + report_heap: BinaryHeap::new(), |
| 84 | + } |
| 85 | + } |
| 86 | + |
| 87 | + fn search(&mut self, movie: i32) -> Vec<i32> { |
| 88 | + let mut empty = BinaryHeap::new(); |
| 89 | + let mut heap = self.search_heaps.get_mut(&movie).unwrap_or(&mut empty); |
| 90 | + let mut res = vec![]; |
| 91 | + |
| 92 | + while let Some(Reverse((price, shop))) = heap.pop() { |
| 93 | + if !self.rented[&(shop, movie)].0 && shop != *res.last().unwrap_or(&-1) { |
| 94 | + res.push(shop); |
| 95 | + } |
| 96 | + |
| 97 | + if res.len() == 5 { |
| 98 | + break; |
| 99 | + } |
| 100 | + } |
| 101 | + |
| 102 | + for &shop in &res { |
| 103 | + heap.push(Reverse((self.rented[&(shop, movie)].1, shop))); |
| 104 | + } |
| 105 | + |
| 106 | + res |
| 107 | + } |
| 108 | + |
| 109 | + fn rent(&mut self, shop: i32, movie: i32) { |
| 110 | + self.rented.get_mut(&(shop, movie)).unwrap().0 = true; |
| 111 | + self.report_heap |
| 112 | + .push(Reverse((self.rented[&(shop, movie)].1, shop, movie))); |
| 113 | + } |
| 114 | + |
| 115 | + fn drop(&mut self, shop: i32, movie: i32) { |
| 116 | + self.rented.get_mut(&(shop, movie)).unwrap().0 = false; |
| 117 | + self.search_heaps |
| 118 | + .get_mut(&movie) |
| 119 | + .unwrap() |
| 120 | + .push(Reverse((self.rented[&(shop, movie)].1, shop))); |
| 121 | + } |
| 122 | + |
| 123 | + fn report(&mut self) -> Vec<Vec<i32>> { |
| 124 | + let mut res = vec![]; |
| 125 | + |
| 126 | + while let Some(Reverse((price, shop, movie))) = self.report_heap.pop() { |
| 127 | + if self.rented[&(shop, movie)].0 && &vec![shop, movie] != res.last().unwrap_or(&vec![]) |
| 128 | + { |
| 129 | + res.push(vec![shop, movie]); |
| 130 | + } |
| 131 | + |
| 132 | + if res.len() == 5 { |
| 133 | + break; |
| 134 | + } |
| 135 | + } |
| 136 | + |
| 137 | + for i in 0..res.len() { |
| 138 | + let (shop, movie) = (res[i][0], res[i][1]); |
| 139 | + self.report_heap |
| 140 | + .push(Reverse((self.rented[&(shop, movie)].1, shop, movie))); |
| 141 | + } |
| 142 | + |
| 143 | + res |
| 144 | + } |
| 145 | +} |
| 146 | + |
| 147 | +/** |
| 148 | + * Your MovieRentingSystem object will be instantiated and called as such: |
| 149 | + * let obj = MovieRentingSystem::new(n, entries); |
| 150 | + * let ret_1: Vec<i32> = obj.search(movie); |
| 151 | + * obj.rent(shop, movie); |
| 152 | + * obj.drop(shop, movie); |
| 153 | + * let ret_4: Vec<Vec<i32>> = obj.report(); |
| 154 | + */ |
| 155 | +``` |
0 commit comments