From fb159f550fa56a490286189cac773c86d3a77be2 Mon Sep 17 00:00:00 2001 From: 0xff-dev Date: Sun, 21 Sep 2025 16:56:47 +0800 Subject: [PATCH] Add solution and test-cases for problem 1912 --- .../1912.Design-Movie-Rental-System/README.md | 49 +++-- .../Solution.go | 181 +++++++++++++++++- .../Solution_test.go | 31 +-- 3 files changed, 233 insertions(+), 28 deletions(-) diff --git a/leetcode/1901-2000/1912.Design-Movie-Rental-System/README.md b/leetcode/1901-2000/1912.Design-Movie-Rental-System/README.md index 6716dc1e5..da572334e 100755 --- a/leetcode/1901-2000/1912.Design-Movie-Rental-System/README.md +++ b/leetcode/1901-2000/1912.Design-Movie-Rental-System/README.md @@ -1,28 +1,45 @@ # [1912.Design Movie Rental System][title] -> [!WARNING|style:flat] -> This question is temporarily unanswered if you have good ideas. Welcome to [Create Pull Request PR](https://github.com/kylesliu/awesome-golang-algorithm) - ## Description +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. -**Example 1:** +Each movie is given as a 2D integer array `entries` where `entries[i] = [shopi, moviei, pricei]` indicates that there is a copy of movie `moviei` at shop `shopi` with a rental price of `pricei`. Each shop carries **at most one** copy of a movie `moviei`. -``` -Input: a = "11", b = "1" -Output: "100" -``` +The system should support the following functions: -## 题意 -> ... +- **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** `shopi` 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. +- **Rent**: Rents an **unrented copy** of a given movie from a given shop. +- **Drop**: Drops off a **previously rented copy** of a given movie at a given shop. +- **Report**: Returns the **cheapest 5 rented movies** (possibly of the same movie ID) as a 2D list `res` where `res[j] = [shopj, moviej]` describes that the `jth` cheapest rented movie `moviei` was rented from the shop `shopj`. The movies in `res` should be sorted by **price** in ascending order, and in case of a tie, the one with the **smaller** shopj should appear first, and if there is still tie, the one with the **smaller** `moviej` 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. -## 题解 +Implement the `MovieRentingSystem` class: -### 思路1 -> ... -Design Movie Rental System -```go -``` +- `MovieRentingSystem(int n, int[][] entries)` Initializes the `MovieRentingSystem` object with `n` shops and the movies in `entries`. +- `List search(int movie)` Returns a list of shops that have an **unrented copy** of the given `movie` as described above. +- `void rent(int shop, int movie)` Rents the given `movie` from the given `shop`. +- `void drop(int shop, int movie)` Drops off a previously rented `movie` at the given `shop`. +- `List> report()` Returns a list of cheapest **rented** movies as described above. +**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. + +**Example 1:** + +``` +Input +["MovieRentingSystem", "search", "rent", "rent", "report", "drop", "search"] +[[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]] +Output +[null, [1, 0, 2], null, null, [[0, 1], [1, 2]], null, [0, 1]] + +Explanation +MovieRentingSystem movieRentingSystem = new MovieRentingSystem(3, [[0, 1, 5], [0, 2, 6], [0, 3, 7], [1, 1, 4], [1, 2, 7], [2, 1, 5]]); +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. +movieRentingSystem.rent(0, 1); // Rent movie 1 from shop 0. Unrented movies at shop 0 are now [2,3]. +movieRentingSystem.rent(1, 2); // Rent movie 2 from shop 1. Unrented movies at shop 1 are now [1]. +movieRentingSystem.report(); // return [[0, 1], [1, 2]]. Movie 1 from shop 0 is cheapest, followed by movie 2 from shop 1. +movieRentingSystem.drop(1, 2); // Drop off movie 2 at shop 1. Unrented movies at shop 1 are now [1,2]. +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. +``` ## 结语 diff --git a/leetcode/1901-2000/1912.Design-Movie-Rental-System/Solution.go b/leetcode/1901-2000/1912.Design-Movie-Rental-System/Solution.go index d115ccf5e..bc7f88d70 100644 --- a/leetcode/1901-2000/1912.Design-Movie-Rental-System/Solution.go +++ b/leetcode/1901-2000/1912.Design-Movie-Rental-System/Solution.go @@ -1,5 +1,184 @@ package Solution -func Solution(x bool) bool { +import ( + "container/heap" + "sort" +) + +type movie struct { + shop, movie, price, index int +} + +type movieList []*movie + +func (m *movieList) Len() int { + return len(*m) +} +func (m *movieList) Swap(i, j int) { + (*m)[i], (*m)[j] = (*m)[j], (*m)[i] + (*m)[i].index = i + (*m)[j].index = j +} + +func (m *movieList) Less(i, j int) bool { + a, b := (*m)[i], (*m)[j] + if a.price != b.price { + return a.price < b.price + } + if a.shop != b.shop { + return a.shop < b.shop + } + return a.movie < b.movie +} + +func (m *movieList) Push(x any) { + mv := x.(*movie) + l := len(*m) + mv.index = l + *m = append(*m, mv) +} + +func (m *movieList) Pop() any { + old := *m + l := len(old) + x := old[l-1] + *m = old[:l-1] return x } + +type MovieRentingSystem struct { + // 每个电影自己的 + movieShops map[int]*movieList + // 已经租界的电影列表 + rentQueue *movieList + index map[[2]int]*movie + + n int +} + +func Constructor(n int, entries [][]int) MovieRentingSystem { + instance := MovieRentingSystem{ + movieShops: make(map[int]*movieList), + rentQueue: &movieList{}, + index: make(map[[2]int]*movie), + n: n, + } + // shop, movie, price + // movie, shop + for _, ent := range entries { + // shop, movie + key := [2]int{ent[0], ent[1]} + if _, ok := instance.movieShops[ent[1]]; !ok { + instance.movieShops[ent[1]] = &movieList{} + } + m := &movie{shop: ent[0], movie: ent[1], price: ent[2], index: 0} + instance.index[key] = m + heap.Push(instance.movieShops[ent[1]], m) + } + return instance +} + +func (this *MovieRentingSystem) Search(m int) []int { + top5 := [][]int{} + list := this.movieShops[m] + if list == nil { + return nil + } + store := make([]*movie, 5) + index := 0 + for list.Len() > 0 && index < 5 { + top := heap.Pop(list).(*movie) + store[index] = top + index++ + top5 = append(top5, []int{top.shop, top.price}) + } + for i := 0; i < index; i++ { + heap.Push(list, store[i]) + } + sort.Slice(top5, func(i, j int) bool { + a, b := top5[i], top5[j] + if a[1] != b[1] { + return a[1] < b[1] + } + return a[0] < b[0] + }) + ret := make([]int, len(top5)) + for i := range top5 { + ret[i] = top5[i][0] + } + + return ret +} + +func (this *MovieRentingSystem) Rent(shop int, m int) { + // 需要记录价格 + key := [2]int{shop, m} + item := this.index[key] + _ = heap.Remove(this.movieShops[m], item.index) + heap.Push(this.rentQueue, item) +} + +func (this *MovieRentingSystem) Drop(shop int, m int) { + key := [2]int{shop, m} + item := this.index[key] + heap.Remove(this.rentQueue, item.index) + heap.Push(this.movieShops[m], item) +} + +func (this *MovieRentingSystem) Report() [][]int { + top5 := [][]int{} + store := make([]*movie, 5) + index := 0 + for this.rentQueue.Len() > 0 && index < 5 { + top := heap.Pop(this.rentQueue).(*movie) + store[index] = top + index++ + top5 = append(top5, []int{top.shop, top.movie, top.price}) + } + for i := 0; i < index; i++ { + heap.Push(this.rentQueue, store[i]) + } + + sort.Slice(top5, func(i, j int) bool { + a, b := top5[i], top5[j] + if a[2] != b[2] { + return a[2] < b[2] + } + if a[0] != b[0] { + return a[0] < b[0] + } + return a[1] < b[1] + }) + ret := make([][]int, len(top5)) + for i := range top5 { + ret[i] = top5[i][:2] + } + + return ret +} + +type op struct { + name string + shop, movie int +} + +func Solution(m int, entries [][]int, ops []op) []any { + c := Constructor(m, entries) + ret := make([]any, 0) + for _, o := range ops { + if o.name == "rent" { + c.Rent(o.shop, o.movie) + continue + } + if o.name == "drop" { + c.Drop(o.shop, o.movie) + continue + } + if o.name == "search" { + ret = append(ret, c.Search(o.movie)) + continue + } + ret = append(ret, c.Report()) + } + return ret +} diff --git a/leetcode/1901-2000/1912.Design-Movie-Rental-System/Solution_test.go b/leetcode/1901-2000/1912.Design-Movie-Rental-System/Solution_test.go index 14ff50eb4..151ff65dc 100644 --- a/leetcode/1901-2000/1912.Design-Movie-Rental-System/Solution_test.go +++ b/leetcode/1901-2000/1912.Design-Movie-Rental-System/Solution_test.go @@ -9,31 +9,40 @@ import ( func TestSolution(t *testing.T) { // 测试用例 cases := []struct { - name string - inputs bool - expect bool + name string + m int + entries [][]int + ops []op + expect []any }{ - {"TestCase", true, true}, - {"TestCase", true, true}, - {"TestCase", false, false}, + {"TestCase1", 3, [][]int{ + {0, 1, 5}, {0, 2, 6}, {0, 3, 7}, {1, 1, 4}, {1, 2, 7}, {2, 1, 5}, + }, []op{ + {name: "search", movie: 1}, + {name: "rent", shop: 0, movie: 1}, + {name: "rent", shop: 1, movie: 2}, + {name: "report"}, + {name: "drop", shop: 1, movie: 2}, + {name: "search", movie: 2}, + }, []any{[]int{1, 0, 2}, [][]int{{0, 1}, {1, 2}}, []int{0, 1}}}, } // 开始测试 for i, c := range cases { t.Run(c.name+" "+strconv.Itoa(i), func(t *testing.T) { - got := Solution(c.inputs) + got := Solution(c.m, c.entries, c.ops) if !reflect.DeepEqual(got, c.expect) { - t.Fatalf("expected: %v, but got: %v, with inputs: %v", - c.expect, got, c.inputs) + t.Fatalf("expected: %v, but got: %v, with inputs: %v %v %v", + c.expect, got, c.m, c.entries, c.ops) } }) } } -// 压力测试 +// 压力测试 func BenchmarkSolution(b *testing.B) { } -// 使用案列 +// 使用案列 func ExampleSolution() { }