Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 33 additions & 16 deletions leetcode/1901-2000/1912.Design-Movie-Rental-System/README.md
Original file line number Diff line number Diff line change
@@ -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<Integer> 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<List<Integer>> 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.
```

## 结语

Expand Down
181 changes: 180 additions & 1 deletion leetcode/1901-2000/1912.Design-Movie-Rental-System/Solution.go
Original file line number Diff line number Diff line change
@@ -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
}
31 changes: 20 additions & 11 deletions leetcode/1901-2000/1912.Design-Movie-Rental-System/Solution_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
}
Loading