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
306 changes: 295 additions & 11 deletions solution/1900-1999/1912.Design Movie Rental System/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,38 +87,60 @@ movieRentingSystem.search(2); // 返回 [0, 1] 。商店 0 和 1 有未借出

<!-- solution:start -->

### 方法一
### 方法一:有序集合

我们定义一个有序集合 $\textit{available}$,其中 $\textit{available}[movie]$ 存储所有未借出的电影 $movie$ 的商店列表,列表中的元素为 $(\textit{price}, \textit{shop})$,并按照 $\textit{price}$ 升序排序,如果 $\textit{price}$ 相同,则按照 $\textit{shop}$ 升序排序。

另外定义一个哈希表 $\textit{price\_map}$,其中 $\textit{price\_map}[f(\textit{shop}, \textit{movie})]$ 存储商店 $\textit{shop}$ 中电影 $\textit{movie}$ 的租借价格。

我们还定义一个有序集合 $\textit{rented}$,其中存储所有已借出的电影,元素为 $(\textit{price}, \textit{shop}, \textit{movie})$,并按照 $\textit{price}$ 升序排序,如果 $\textit{price}$ 相同,则按照 $\textit{shop}$ 升序排序,如果 $\textit{shop}$ 也相同,则按照 $\textit{movie}$ 升序排序。

对于 $\text{MovieRentingSystem}(n, \text{entries})$ 操作,我们遍历 $\text{entries}$,将每个商店的电影信息加入到 $\textit{available}$ 和 $\textit{price\_map}$ 中。时间复杂度为 $O(m \log m)$,其中 $m$ 是 $\text{entries}$ 的长度。

对于 $\text{search}(\text{movie})$ 操作,我们返回 $\textit{available}[\text{movie}]$ 中前 5 个商店的编号。时间复杂度为 $O(1)$。

对于 $\text{rent}(\text{shop}, \text{movie})$ 操作,我们从 $\textit{available}[\text{movie}]$ 中移除 $(\textit{price}, \textit{shop})$,并将 $(\textit{price}, \textit{shop}, \textit{movie})$ 加入到 $\textit{rented}$ 中。时间复杂度为 $O(\log m)$。

对于 $\text{drop}(\text{shop}, \text{movie})$ 操作,我们从 $\textit{rented}$ 中移除 $(\textit{price}, \textit{shop}, \textit{movie})$,并将 $(\textit{price}, \textit{shop})$ 加入到 $\textit{available}[\text{movie}]$ 中。时间复杂度为 $O(\log m)$。

对于 $\text{report}()$ 操作,我们返回 $\textit{rented}$ 中前 5 个已借出电影的商店编号和电影编号。时间复杂度为 $O(1)$。

空间复杂度为 $O(m)$。其中 $m$ 是 $\text{entries}$ 的长度。

<!-- tabs:start -->

#### Python3

```python
class MovieRentingSystem:

def __init__(self, n: int, entries: List[List[int]]):
self.unrented = collections.defaultdict(SortedList) # {movie: (price, shop)}
self.shopAndMovieToPrice = {} # {(shop, movie): price}
self.rented = SortedList() # (price, shop, movie)
self.available = defaultdict(lambda: SortedList())
self.price_map = {}
for shop, movie, price in entries:
self.unrented[movie].add((price, shop))
self.shopAndMovieToPrice[(shop, movie)] = price
self.available[movie].add((price, shop))
self.price_map[self.f(shop, movie)] = price
self.rented = SortedList()

def search(self, movie: int) -> List[int]:
return [shop for _, shop in self.unrented[movie][:5]]
return [shop for _, shop in self.available[movie][:5]]

def rent(self, shop: int, movie: int) -> None:
price = self.shopAndMovieToPrice[(shop, movie)]
self.unrented[movie].remove((price, shop))
price = self.price_map[self.f(shop, movie)]
self.available[movie].remove((price, shop))
self.rented.add((price, shop, movie))

def drop(self, shop: int, movie: int) -> None:
price = self.shopAndMovieToPrice[(shop, movie)]
self.unrented[movie].add((price, shop))
price = self.price_map[self.f(shop, movie)]
self.rented.remove((price, shop, movie))
self.available[movie].add((price, shop))

def report(self) -> List[List[int]]:
return [[shop, movie] for _, shop, movie in self.rented[:5]]

def f(self, shop: int, movie: int) -> int:
return shop << 30 | movie


# Your MovieRentingSystem object will be instantiated and called as such:
# obj = MovieRentingSystem(n, entries)
Expand All @@ -128,6 +150,268 @@ class MovieRentingSystem:
# param_4 = obj.report()
```

#### Java

```java
class MovieRentingSystem {
private Map<Integer, TreeSet<int[]>> available = new HashMap<>();
private Map<Long, Integer> priceMap = new HashMap<>();
private TreeSet<int[]> rented = new TreeSet<>((a, b) -> {
if (a[0] != b[0]) {
return a[0] - b[0];
}
if (a[1] != b[1]) {
return a[1] - b[1];
}
return a[2] - b[2];
});

public MovieRentingSystem(int n, int[][] entries) {
for (int[] entry : entries) {
int shop = entry[0], movie = entry[1], price = entry[2];
available
.computeIfAbsent(movie, k -> new TreeSet<>((a, b) -> {
if (a[0] != b[0]) {
return a[0] - b[0];
}
return a[1] - b[1];
}))
.add(new int[] {price, shop});
priceMap.put(f(shop, movie), price);
}
}

public List<Integer> search(int movie) {
List<Integer> res = new ArrayList<>();
if (!available.containsKey(movie)) {
return res;
}
int cnt = 0;
for (int[] item : available.get(movie)) {
res.add(item[1]);
if (++cnt == 5) {
break;
}
}
return res;
}

public void rent(int shop, int movie) {
int price = priceMap.get(f(shop, movie));
available.get(movie).remove(new int[] {price, shop});
rented.add(new int[] {price, shop, movie});
}

public void drop(int shop, int movie) {
int price = priceMap.get(f(shop, movie));
rented.remove(new int[] {price, shop, movie});
available.get(movie).add(new int[] {price, shop});
}

public List<List<Integer>> report() {
List<List<Integer>> res = new ArrayList<>();
int cnt = 0;
for (int[] item : rented) {
res.add(Arrays.asList(item[1], item[2]));
if (++cnt == 5) {
break;
}
}
return res;
}

private long f(int shop, int movie) {
return ((long) shop << 30) | movie;
}
}

/**
* Your MovieRentingSystem object will be instantiated and called as such:
* MovieRentingSystem obj = new MovieRentingSystem(n, entries);
* List<Integer> param_1 = obj.search(movie);
* obj.rent(shop,movie);
* obj.drop(shop,movie);
* List<List<Integer>> param_4 = obj.report();
*/
```

#### C++

```cpp
class MovieRentingSystem {
private:
unordered_map<int, set<pair<int, int>>> available; // movie -> {(price, shop)}
unordered_map<long long, int> priceMap;
set<tuple<int, int, int>> rented; // {(price, shop, movie)}

long long f(int shop, int movie) {
return ((long long) shop << 30) | movie;
}

public:
MovieRentingSystem(int n, vector<vector<int>>& entries) {
for (auto& e : entries) {
int shop = e[0], movie = e[1], price = e[2];
available[movie].insert({price, shop});
priceMap[f(shop, movie)] = price;
}
}

vector<int> search(int movie) {
vector<int> res;
if (!available.count(movie)) {
return res;
}
int cnt = 0;
for (auto& [price, shop] : available[movie]) {
res.push_back(shop);
if (++cnt == 5) {
break;
}
}
return res;
}

void rent(int shop, int movie) {
int price = priceMap[f(shop, movie)];
available[movie].erase({price, shop});
rented.insert({price, shop, movie});
}

void drop(int shop, int movie) {
int price = priceMap[f(shop, movie)];
rented.erase({price, shop, movie});
available[movie].insert({price, shop});
}

vector<vector<int>> report() {
vector<vector<int>> res;
int cnt = 0;
for (auto& [price, shop, movie] : rented) {
res.push_back({shop, movie});
if (++cnt == 5) {
break;
}
}
return res;
}
};

/**
* Your MovieRentingSystem object will be instantiated and called as such:
* MovieRentingSystem* obj = new MovieRentingSystem(n, entries);
* vector<int> param_1 = obj->search(movie);
* obj->rent(shop,movie);
* obj->drop(shop,movie);
* vector<vector<int>> param_4 = obj->report();
*/
```

#### Go

```go
type MovieRentingSystem struct {
available map[int]*treeset.Set // movie -> (price, shop)
priceMap map[int64]int
rented *treeset.Set // (price, shop, movie)
}

func Constructor(n int, entries [][]int) MovieRentingSystem {
// comparator for (price, shop)
cmpAvail := func(a, b any) int {
x := a.([2]int)
y := b.([2]int)
if x[0] != y[0] {
return x[0] - y[0]
}
return x[1] - y[1]
}
// comparator for (price, shop, movie)
cmpRented := func(a, b any) int {
x := a.([3]int)
y := b.([3]int)
if x[0] != y[0] {
return x[0] - y[0]
}
if x[1] != y[1] {
return x[1] - y[1]
}
return x[2] - y[2]
}

mrs := MovieRentingSystem{
available: make(map[int]*treeset.Set),
priceMap: make(map[int64]int),
rented: treeset.NewWith(cmpRented),
}

for _, e := range entries {
shop, movie, price := e[0], e[1], e[2]
if _, ok := mrs.available[movie]; !ok {
mrs.available[movie] = treeset.NewWith(cmpAvail)
}
mrs.available[movie].Add([2]int{price, shop})
mrs.priceMap[f(shop, movie)] = price
}

return mrs
}

func (this *MovieRentingSystem) Search(movie int) []int {
res := []int{}
if _, ok := this.available[movie]; !ok {
return res
}
it := this.available[movie].Iterator()
it.Begin()
cnt := 0
for it.Next() && cnt < 5 {
pair := it.Value().([2]int)
res = append(res, pair[1])
cnt++
}
return res
}

func (this *MovieRentingSystem) Rent(shop int, movie int) {
price := this.priceMap[f(shop, movie)]
this.available[movie].Remove([2]int{price, shop})
this.rented.Add([3]int{price, shop, movie})
}

func (this *MovieRentingSystem) Drop(shop int, movie int) {
price := this.priceMap[f(shop, movie)]
this.rented.Remove([3]int{price, shop, movie})
this.available[movie].Add([2]int{price, shop})
}

func (this *MovieRentingSystem) Report() [][]int {
res := [][]int{}
it := this.rented.Iterator()
it.Begin()
cnt := 0
for it.Next() && cnt < 5 {
t := it.Value().([3]int)
res = append(res, []int{t[1], t[2]})
cnt++
}
return res
}

func f(shop, movie int) int64 {
return (int64(shop) << 30) | int64(movie)
}

/**
* Your MovieRentingSystem object will be instantiated and called as such:
* obj := Constructor(n, entries);
* param_1 := obj.Search(movie);
* obj.Rent(shop,movie);
* obj.Drop(shop,movie);
* param_4 := obj.Report();
*/
```

<!-- tabs:end -->

<!-- solution:end -->
Expand Down
Loading