From e9c6b441a5b687cd8c0ba6eb1db04122753544dd Mon Sep 17 00:00:00 2001 From: yanglbme Date: Fri, 3 Nov 2023 16:22:37 +0800 Subject: [PATCH] feat: add solutions to lc problems: No.2907,2921 * No.2907.Maximum Profitable Triplets With Increasing Prices I * No.2921.Maximum Profitable Triplets With Increasing Prices II --- .../README.md | 2 +- .../0115.Distinct Subsequences/README_EN.md | 26 + .../README.md | 690 ++++++++++++++++++ .../README_EN.md | 690 ++++++++++++++++++ .../README.md | 350 ++++++++- .../README_EN.md | 350 ++++++++- .../Solution.cpp | 56 ++ .../Solution.go | 60 ++ .../Solution.java | 56 ++ .../Solution.py | 39 + .../Solution.rs | 64 ++ .../Solution.ts | 57 ++ 12 files changed, 2433 insertions(+), 7 deletions(-) create mode 100644 solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.cpp create mode 100644 solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.go create mode 100644 solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.java create mode 100644 solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.py create mode 100644 solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.rs create mode 100644 solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.ts diff --git a/solution/0100-0199/0114.Flatten Binary Tree to Linked List/README.md b/solution/0100-0199/0114.Flatten Binary Tree to Linked List/README.md index 6dc79846ae2c2..4341b775b1755 100644 --- a/solution/0100-0199/0114.Flatten Binary Tree to Linked List/README.md +++ b/solution/0100-0199/0114.Flatten Binary Tree to Linked List/README.md @@ -59,7 +59,7 @@ 因此,对于当前节点,如果其左子节点不为空,我们找到左子树的最右节点,作为前驱节点,然后将当前节点的右子节点赋给前驱节点的右子节点。然后将当前节点的左子节点赋给当前节点的右子节点,并将当前节点的左子节点置为空。然后将当前节点的右子节点作为下一个节点,继续处理,直至所有节点处理完毕。 -时间复杂度 $O(n)$,空间复杂度 O(1)$。其中 $n$ 是树中节点的个数。 +时间复杂度 $O(n)$,其中 $n$ 是树中节点的个数。空间复杂度 O(1)$。 diff --git a/solution/0100-0199/0115.Distinct Subsequences/README_EN.md b/solution/0100-0199/0115.Distinct Subsequences/README_EN.md index 5c446cab101ce..70d713ae688e1 100644 --- a/solution/0100-0199/0115.Distinct Subsequences/README_EN.md +++ b/solution/0100-0199/0115.Distinct Subsequences/README_EN.md @@ -44,6 +44,32 @@ As shown below, there are 5 ways you can generate "bag" from s. ## Solutions +**Solution 1: Dynamic Programming** + +We define $f[i][j]$ as the number of schemes where the first $i$ characters of string $s$ form the first $j$ characters of string $t$. Initially, $f[i][0]=1$ for all $i \in [0,m]$. + +When $i > 0$, we consider the calculation of $f[i][j]$: + +- When $s[i-1] \ne t[j-1]$, we cannot select $s[i-1]$, so $f[i][j]=f[i-1][j]$; +- Otherwise, we can select $s[i-1]$, so $f[i][j]=f[i-1][j-1]$. + +Therefore, we have the following state transition equation: + +$$ +f[i][j]=\left\{ +\begin{aligned} +&f[i-1][j], &s[i-1] \ne t[j-1] \\ +&f[i-1][j-1]+f[i-1][j], &s[i-1]=t[j-1] +\end{aligned} +\right. +$$ + +The final answer is $f[m][n]$, where $m$ and $n$ are the lengths of strings $s$ and $t$ respectively. + +The time complexity is $O(m \times n)$, and the space complexity is $O(m \times n)$. + +We notice that the calculation of $f[i][j]$ is only related to $f[i-1][..]$. Therefore, we can optimize the first dimension, reducing the space complexity to $O(n)$. + ### **Python3** diff --git a/solution/2900-2999/2907.Maximum Profitable Triplets With Increasing Prices I/README.md b/solution/2900-2999/2907.Maximum Profitable Triplets With Increasing Prices I/README.md index bdaca0afda343..f92d84030fbd2 100644 --- a/solution/2900-2999/2907.Maximum Profitable Triplets With Increasing Prices I/README.md +++ b/solution/2900-2999/2907.Maximum Profitable Triplets With Increasing Prices I/README.md @@ -66,6 +66,14 @@ 时间复杂度 $O(n^2)$,其中 $n$ 为数组长度。空间复杂度 $O(1)$。 +**方法二:树状数组** + +我们可以用两个树状数组分别维护每个价格左边以及右边的最大利润,然后枚举中间的价格,通过树状数组查询左右两边的最大利润,最后取最大值即可。 + +时间复杂度 $O(n \times \log M)$,空间复杂度 $O(M)$。其中 $n$ 是数组 $prices$ 的长度,而 $M$ 是数组 $prices$ 中的最大值,本题中 $M \le 10^6$。 + +由于 $prices$ 的长度不超过 $2000$,而 $prices[i]$ 的取值达到 $10^6$,因此,我们可以对 $prices$ 进行离散化处理,这样可以将空间复杂度降低到 $O(n)$。 + ### **Python3** @@ -90,6 +98,92 @@ class Solution: return ans ``` +```python +class BinaryIndexedTree: + def __init__(self, n: int): + self.n = n + self.c = [0] * (n + 1) + + def update(self, x: int, v: int): + while x <= self.n: + self.c[x] = max(self.c[x], v) + x += x & -x + + def query(self, x: int) -> int: + mx = 0 + while x: + mx = max(mx, self.c[x]) + x -= x & -x + return mx + + +class Solution: + def maxProfit(self, prices: List[int], profits: List[int]) -> int: + n = len(prices) + left = [0] * n + right = [0] * n + + m = max(prices) + tree1 = BinaryIndexedTree(m + 1) + tree2 = BinaryIndexedTree(m + 1) + + for i, x in enumerate(prices): + left[i] = tree1.query(x - 1) + tree1.update(x, profits[i]) + for i in range(n - 1, -1, -1): + x = m + 1 - prices[i] + right[i] = tree2.query(x - 1) + tree2.update(x, profits[i]) + + return max( + (l + x + r for l, x, r in zip(left, profits, right) if l and r), default=-1 + ) +``` + +```python +class BinaryIndexedTree: + def __init__(self, n: int): + self.n = n + self.c = [0] * (n + 1) + + def update(self, x: int, v: int): + while x <= self.n: + self.c[x] = max(self.c[x], v) + x += x & -x + + def query(self, x: int) -> int: + mx = 0 + while x: + mx = max(mx, self.c[x]) + x -= x & -x + return mx + + +class Solution: + def maxProfit(self, prices: List[int], profits: List[int]) -> int: + n = len(prices) + left = [0] * n + right = [0] * n + + s = sorted(set(prices)) + m = len(s) + tree1 = BinaryIndexedTree(m + 1) + tree2 = BinaryIndexedTree(m + 1) + + for i, x in enumerate(prices): + x = bisect_left(s, x) + 1 + left[i] = tree1.query(x - 1) + tree1.update(x, profits[i]) + for i in range(n - 1, -1, -1): + x = m + 1 - (bisect_left(s, prices[i]) + 1) + right[i] = tree2.query(x - 1) + tree2.update(x, profits[i]) + + return max( + (l + x + r for l, x, r in zip(left, profits, right) if l and r), default=-1 + ) +``` + ### **Java** @@ -120,6 +214,141 @@ class Solution { } ``` +```java +class BinaryIndexedTree { + private int n; + private int[] c; + + public BinaryIndexedTree(int n) { + this.n = n; + c = new int[n + 1]; + } + + public void update(int x, int v) { + while (x <= n) { + c[x] = Math.max(c[x], v); + x += x & -x; + } + } + + public int query(int x) { + int mx = 0; + while (x > 0) { + mx = Math.max(mx, c[x]); + x -= x & -x; + } + return mx; + } +} + +class Solution { + public int maxProfit(int[] prices, int[] profits) { + int n = prices.length; + int[] left = new int[n]; + int[] right = new int[n]; + int m = 0; + for (int x : prices) { + m = Math.max(m, x); + } + BinaryIndexedTree tree1 = new BinaryIndexedTree(m + 1); + BinaryIndexedTree tree2 = new BinaryIndexedTree(m + 1); + for (int i = 0; i < n; ++i) { + int x = prices[i]; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + for (int i = n - 1; i >= 0; --i) { + int x = m + 1 - prices[i]; + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + int ans = -1; + for (int i = 0; i < n; ++i) { + if (left[i] > 0 && right[i] > 0) { + ans = Math.max(ans, left[i] + profits[i] + right[i]); + } + } + return ans; + } +} +``` + +```java +class BinaryIndexedTree { + private int n; + private int[] c; + + public BinaryIndexedTree(int n) { + this.n = n; + c = new int[n + 1]; + } + + public void update(int x, int v) { + while (x <= n) { + c[x] = Math.max(c[x], v); + x += x & -x; + } + } + + public int query(int x) { + int mx = 0; + while (x > 0) { + mx = Math.max(mx, c[x]); + x -= x & -x; + } + return mx; + } +} + +class Solution { + public int maxProfit(int[] prices, int[] profits) { + int n = prices.length; + int[] left = new int[n]; + int[] right = new int[n]; + int[] s = prices.clone(); + Arrays.sort(s); + int m = 0; + for (int i = 0; i < n; ++i) { + if (i == 0 || s[i] != s[i - 1]) { + s[m++] = s[i]; + } + } + BinaryIndexedTree tree1 = new BinaryIndexedTree(m + 1); + BinaryIndexedTree tree2 = new BinaryIndexedTree(m + 1); + for (int i = 0; i < n; ++i) { + int x = search(s, prices[i], m); + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + for (int i = n - 1; i >= 0; --i) { + int x = m + 1 - search(s, prices[i], m); + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + int ans = -1; + for (int i = 0; i < n; ++i) { + if (left[i] > 0 && right[i] > 0) { + ans = Math.max(ans, left[i] + profits[i] + right[i]); + } + } + return ans; + } + + private int search(int[] nums, int x, int r) { + int l = 0; + while (l < r) { + int mid = (l + r) >> 1; + if (nums[mid] >= x) { + r = mid; + } else { + l = mid + 1; + } + } + return l + 1; + } +} +``` + ### **C++** ```cpp @@ -149,6 +378,127 @@ public: }; ``` +```cpp +class BinaryIndexedTree { +private: + int n; + vector c; + +public: + BinaryIndexedTree(int n) { + this->n = n; + c.resize(n + 1, 0); + } + + void update(int x, int v) { + while (x <= n) { + c[x] = max(c[x], v); + x += x & -x; + } + } + + int query(int x) { + int mx = 0; + while (x > 0) { + mx = max(mx, c[x]); + x -= x & -x; + } + return mx; + } +}; + +class Solution { +public: + int maxProfit(vector& prices, vector& profits) { + int n = prices.size(); + vector left(n, 0); + vector right(n, 0); + int m = *max_element(prices.begin(), prices.end()); + BinaryIndexedTree tree1(m + 1); + BinaryIndexedTree tree2(m + 1); + for (int i = 0; i < n; ++i) { + int x = prices[i]; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + for (int i = n - 1; i >= 0; --i) { + int x = m + 1 - prices[i]; + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + int ans = -1; + for (int i = 0; i < n; ++i) { + if (left[i] > 0 && right[i] > 0) { + ans = max(ans, left[i] + profits[i] + right[i]); + } + } + return ans; + } +}; +``` + +```cpp +class BinaryIndexedTree { +private: + int n; + vector c; + +public: + BinaryIndexedTree(int n) { + this->n = n; + c.resize(n + 1, 0); + } + + void update(int x, int v) { + while (x <= n) { + c[x] = max(c[x], v); + x += x & -x; + } + } + + int query(int x) { + int mx = 0; + while (x > 0) { + mx = max(mx, c[x]); + x -= x & -x; + } + return mx; + } +}; + +class Solution { +public: + int maxProfit(vector& prices, vector& profits) { + int n = prices.size(); + vector left(n); + vector right(n); + vector s = prices; + sort(s.begin(), s.end()); + s.erase(unique(s.begin(), s.end()), s.end()); + int m = s.size(); + BinaryIndexedTree tree1(m + 1); + BinaryIndexedTree tree2(m + 1); + for (int i = 0; i < n; ++i) { + int x = lower_bound(s.begin(), s.end(), prices[i]) - s.begin() + 1; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + for (int i = n - 1; i >= 0; --i) { + int x = m + 1 - (lower_bound(s.begin(), s.end(), prices[i]) - s.begin() + 1); + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + int ans = -1; + for (int i = 0; i < n; ++i) { + if (left[i] > 0 && right[i] > 0) { + ans = max(ans, left[i] + profits[i] + right[i]); + } + } + return ans; + } +}; +``` + ### **Go** ```go @@ -175,6 +525,138 @@ func maxProfit(prices []int, profits []int) int { } ``` +```go +type BinaryIndexedTree struct { + n int + c []int +} + +func NewBinaryIndexedTree(n int) BinaryIndexedTree { + c := make([]int, n+1) + return BinaryIndexedTree{n: n, c: c} +} + +func (bit *BinaryIndexedTree) update(x, v int) { + for x <= bit.n { + bit.c[x] = max(bit.c[x], v) + x += x & -x + } +} + +func (bit *BinaryIndexedTree) query(x int) int { + mx := 0 + for x > 0 { + mx = max(mx, bit.c[x]) + x -= x & -x + } + return mx +} + +func maxProfit(prices []int, profits []int) int { + n := len(prices) + left := make([]int, n) + right := make([]int, n) + m := 0 + + for _, x := range prices { + m = max(m, x) + } + + tree1 := NewBinaryIndexedTree(m + 1) + tree2 := NewBinaryIndexedTree(m + 1) + + for i, x := range prices { + left[i] = tree1.query(x - 1) + tree1.update(x, profits[i]) + } + + for i := n - 1; i >= 0; i-- { + x := m + 1 - prices[i] + right[i] = tree2.query(x - 1) + tree2.update(x, profits[i]) + } + + ans := -1 + + for i := 0; i < n; i++ { + if left[i] > 0 && right[i] > 0 { + ans = max(ans, left[i]+profits[i]+right[i]) + } + } + + return ans +} +``` + +```go +type BinaryIndexedTree struct { + n int + c []int +} + +func NewBinaryIndexedTree(n int) BinaryIndexedTree { + c := make([]int, n+1) + return BinaryIndexedTree{n: n, c: c} +} + +func (bit *BinaryIndexedTree) update(x, v int) { + for x <= bit.n { + bit.c[x] = max(bit.c[x], v) + x += x & -x + } +} + +func (bit *BinaryIndexedTree) query(x int) int { + mx := 0 + for x > 0 { + mx = max(mx, bit.c[x]) + x -= x & -x + } + return mx +} + +func maxProfit(prices []int, profits []int) int { + n := len(prices) + left := make([]int, n) + right := make([]int, n) + s := make([]int, n) + copy(s, prices) + sort.Ints(s) + m := 0 + for i, x := range s { + if i == 0 || x != s[i-1] { + s[m] = x + m++ + } + } + + tree1 := NewBinaryIndexedTree(m + 1) + tree2 := NewBinaryIndexedTree(m + 1) + + for i, x := range prices { + x = sort.SearchInts(s[:m], x) + 1 + left[i] = tree1.query(x - 1) + tree1.update(x, profits[i]) + } + + for i := n - 1; i >= 0; i-- { + x := m + 1 - (sort.SearchInts(s[:m], prices[i]) + 1) + right[i] = tree2.query(x - 1) + tree2.update(x, profits[i]) + } + + ans := -1 + + for i := 0; i < n; i++ { + if left[i] > 0 && right[i] > 0 { + ans = max(ans, left[i]+profits[i]+right[i]) + } + } + + return ans +} +``` + ### **TypeScript** ```ts @@ -201,6 +683,147 @@ function maxProfit(prices: number[], profits: number[]): number { } ``` +```ts +class BinaryIndexedTree { + private n: number; + private c: number[]; + + constructor(n: number) { + this.n = n; + this.c = Array(n + 1).fill(0); + } + + update(x: number, v: number): void { + while (x <= this.n) { + this.c[x] = Math.max(this.c[x], v); + x += x & -x; + } + } + + query(x: number): number { + let mx = 0; + while (x > 0) { + mx = Math.max(mx, this.c[x]); + x -= x & -x; + } + return mx; + } +} + +function maxProfit(prices: number[], profits: number[]): number { + const n: number = prices.length; + const left: number[] = Array(n).fill(0); + const right: number[] = Array(n).fill(0); + const m = Math.max(...prices); + + const tree1: BinaryIndexedTree = new BinaryIndexedTree(m + 1); + const tree2: BinaryIndexedTree = new BinaryIndexedTree(m + 1); + + for (let i = 0; i < n; i++) { + const x: number = prices[i]; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + + for (let i = n - 1; i >= 0; i--) { + const x: number = m + 1 - prices[i]; + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + + let ans: number = -1; + + for (let i = 0; i < n; i++) { + if (left[i] > 0 && right[i] > 0) { + ans = Math.max(ans, left[i] + profits[i] + right[i]); + } + } + + return ans; +} +``` + +```ts +class BinaryIndexedTree { + private n: number; + private c: number[]; + + constructor(n: number) { + this.n = n; + this.c = Array(n + 1).fill(0); + } + + update(x: number, v: number): void { + while (x <= this.n) { + this.c[x] = Math.max(this.c[x], v); + x += x & -x; + } + } + + query(x: number): number { + let mx = 0; + while (x > 0) { + mx = Math.max(mx, this.c[x]); + x -= x & -x; + } + return mx; + } +} + +function maxProfit(prices: number[], profits: number[]): number { + const n: number = prices.length; + const left: number[] = Array(n).fill(0); + const right: number[] = Array(n).fill(0); + + const s = [...prices].sort((a, b) => a - b); + let m = 0; + for (let i = 0; i < n; ++i) { + if (i === 0 || s[i] !== s[i - 1]) { + s[m++] = s[i]; + } + } + s.length = m; + + const tree1: BinaryIndexedTree = new BinaryIndexedTree(m + 1); + const tree2: BinaryIndexedTree = new BinaryIndexedTree(m + 1); + + const search = (nums: number[], x: number): number => { + let [l, r] = [0, nums.length]; + while (l < r) { + const mid = (l + r) >> 1; + if (nums[mid] >= x) { + r = mid; + } else { + l = mid + 1; + } + } + return l; + }; + + for (let i = 0; i < n; ++i) { + const x = search(s, prices[i]) + 1; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + + for (let i = n - 1; i >= 0; i--) { + const x: number = m + 1 - (search(s, prices[i]) + 1); + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + + let ans: number = -1; + + for (let i = 0; i < n; i++) { + if (left[i] > 0 && right[i] > 0) { + ans = Math.max(ans, left[i] + profits[i] + right[i]); + } + } + + return ans; +} +``` + ### **Rust** ```rust @@ -235,6 +858,73 @@ impl Solution { } ``` +```rust +struct BinaryIndexedTree { + n: usize, + c: Vec, +} + +impl BinaryIndexedTree { + fn new(n: usize) -> BinaryIndexedTree { + BinaryIndexedTree { + n, + c: vec![0; n + 1], + } + } + + fn update(&mut self, x: usize, v: i32) { + let mut x = x; + while x <= self.n { + self.c[x] = self.c[x].max(v); + x += x & x.wrapping_neg(); + } + } + + fn query(&self, x: usize) -> i32 { + let mut x = x; + let mut mx = 0; + while x > 0 { + mx = mx.max(self.c[x]); + x -= x & x.wrapping_neg(); + } + mx + } +} + +impl Solution { + pub fn max_profit(prices: Vec, profits: Vec) -> i32 { + let n = prices.len(); + let mut left = vec![0; n]; + let mut right = vec![0; n]; + let m = prices.iter().cloned().max().unwrap_or(0); + + let mut tree1 = BinaryIndexedTree::new(m as usize + 1); + let mut tree2 = BinaryIndexedTree::new(m as usize + 1); + + for i in 0..n { + let x = prices[i] as usize; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + + for i in (0..n).rev() { + let x = (m + 1 - prices[i]) as usize; + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + + let mut ans = -1; + for i in 0..n { + if left[i] > 0 && right[i] > 0 { + ans = ans.max(left[i] + profits[i] + right[i]); + } + } + + ans + } +} +``` + ### **...** ``` diff --git a/solution/2900-2999/2907.Maximum Profitable Triplets With Increasing Prices I/README_EN.md b/solution/2900-2999/2907.Maximum Profitable Triplets With Increasing Prices I/README_EN.md index 4dc6f70947d10..4261d2f61cd73 100644 --- a/solution/2900-2999/2907.Maximum Profitable Triplets With Increasing Prices I/README_EN.md +++ b/solution/2900-2999/2907.Maximum Profitable Triplets With Increasing Prices I/README_EN.md @@ -60,6 +60,14 @@ We can enumerate the middle element $profits[j]$, and then enumerate the left el The time complexity is $O(n^2)$, where $n$ is the length of the array. The space complexity is $O(1)$. +**Solution 2: Binary Indexed Tree** + +We can use two Binary Indexed Trees (BITs) to maintain the maximum profit on the left and right of each price, respectively. Then, we enumerate the middle price, query the maximum profit on both sides through the BIT, and finally take the maximum value. + +The time complexity is $O(n \times \log M)$, and the space complexity is $O(M)$. Here, $n$ is the length of the array $prices$, and $M$ is the maximum value in the array $prices$. In this problem, $M \le 10^6$. + +Since the length of $prices$ does not exceed $2000$, and the value of $prices[i]$ reaches $10^6$, we can discretize $prices$, which can reduce the space complexity to $O(n)$. + ### **Python3** @@ -82,6 +90,92 @@ class Solution: return ans ``` +```python +class BinaryIndexedTree: + def __init__(self, n: int): + self.n = n + self.c = [0] * (n + 1) + + def update(self, x: int, v: int): + while x <= self.n: + self.c[x] = max(self.c[x], v) + x += x & -x + + def query(self, x: int) -> int: + mx = 0 + while x: + mx = max(mx, self.c[x]) + x -= x & -x + return mx + + +class Solution: + def maxProfit(self, prices: List[int], profits: List[int]) -> int: + n = len(prices) + left = [0] * n + right = [0] * n + + m = max(prices) + tree1 = BinaryIndexedTree(m + 1) + tree2 = BinaryIndexedTree(m + 1) + + for i, x in enumerate(prices): + left[i] = tree1.query(x - 1) + tree1.update(x, profits[i]) + for i in range(n - 1, -1, -1): + x = m + 1 - prices[i] + right[i] = tree2.query(x - 1) + tree2.update(x, profits[i]) + + return max( + (l + x + r for l, x, r in zip(left, profits, right) if l and r), default=-1 + ) +``` + +```python +class BinaryIndexedTree: + def __init__(self, n: int): + self.n = n + self.c = [0] * (n + 1) + + def update(self, x: int, v: int): + while x <= self.n: + self.c[x] = max(self.c[x], v) + x += x & -x + + def query(self, x: int) -> int: + mx = 0 + while x: + mx = max(mx, self.c[x]) + x -= x & -x + return mx + + +class Solution: + def maxProfit(self, prices: List[int], profits: List[int]) -> int: + n = len(prices) + left = [0] * n + right = [0] * n + + s = sorted(set(prices)) + m = len(s) + tree1 = BinaryIndexedTree(m + 1) + tree2 = BinaryIndexedTree(m + 1) + + for i, x in enumerate(prices): + x = bisect_left(s, x) + 1 + left[i] = tree1.query(x - 1) + tree1.update(x, profits[i]) + for i in range(n - 1, -1, -1): + x = m + 1 - (bisect_left(s, prices[i]) + 1) + right[i] = tree2.query(x - 1) + tree2.update(x, profits[i]) + + return max( + (l + x + r for l, x, r in zip(left, profits, right) if l and r), default=-1 + ) +``` + ### **Java** ```java @@ -110,6 +204,141 @@ class Solution { } ``` +```java +class BinaryIndexedTree { + private int n; + private int[] c; + + public BinaryIndexedTree(int n) { + this.n = n; + c = new int[n + 1]; + } + + public void update(int x, int v) { + while (x <= n) { + c[x] = Math.max(c[x], v); + x += x & -x; + } + } + + public int query(int x) { + int mx = 0; + while (x > 0) { + mx = Math.max(mx, c[x]); + x -= x & -x; + } + return mx; + } +} + +class Solution { + public int maxProfit(int[] prices, int[] profits) { + int n = prices.length; + int[] left = new int[n]; + int[] right = new int[n]; + int m = 0; + for (int x : prices) { + m = Math.max(m, x); + } + BinaryIndexedTree tree1 = new BinaryIndexedTree(m + 1); + BinaryIndexedTree tree2 = new BinaryIndexedTree(m + 1); + for (int i = 0; i < n; ++i) { + int x = prices[i]; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + for (int i = n - 1; i >= 0; --i) { + int x = m + 1 - prices[i]; + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + int ans = -1; + for (int i = 0; i < n; ++i) { + if (left[i] > 0 && right[i] > 0) { + ans = Math.max(ans, left[i] + profits[i] + right[i]); + } + } + return ans; + } +} +``` + +```java +class BinaryIndexedTree { + private int n; + private int[] c; + + public BinaryIndexedTree(int n) { + this.n = n; + c = new int[n + 1]; + } + + public void update(int x, int v) { + while (x <= n) { + c[x] = Math.max(c[x], v); + x += x & -x; + } + } + + public int query(int x) { + int mx = 0; + while (x > 0) { + mx = Math.max(mx, c[x]); + x -= x & -x; + } + return mx; + } +} + +class Solution { + public int maxProfit(int[] prices, int[] profits) { + int n = prices.length; + int[] left = new int[n]; + int[] right = new int[n]; + int[] s = prices.clone(); + Arrays.sort(s); + int m = 0; + for (int i = 0; i < n; ++i) { + if (i == 0 || s[i] != s[i - 1]) { + s[m++] = s[i]; + } + } + BinaryIndexedTree tree1 = new BinaryIndexedTree(m + 1); + BinaryIndexedTree tree2 = new BinaryIndexedTree(m + 1); + for (int i = 0; i < n; ++i) { + int x = search(s, prices[i], m); + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + for (int i = n - 1; i >= 0; --i) { + int x = m + 1 - search(s, prices[i], m); + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + int ans = -1; + for (int i = 0; i < n; ++i) { + if (left[i] > 0 && right[i] > 0) { + ans = Math.max(ans, left[i] + profits[i] + right[i]); + } + } + return ans; + } + + private int search(int[] nums, int x, int r) { + int l = 0; + while (l < r) { + int mid = (l + r) >> 1; + if (nums[mid] >= x) { + r = mid; + } else { + l = mid + 1; + } + } + return l + 1; + } +} +``` + ### **C++** ```cpp @@ -139,6 +368,127 @@ public: }; ``` +```cpp +class BinaryIndexedTree { +private: + int n; + vector c; + +public: + BinaryIndexedTree(int n) { + this->n = n; + c.resize(n + 1, 0); + } + + void update(int x, int v) { + while (x <= n) { + c[x] = max(c[x], v); + x += x & -x; + } + } + + int query(int x) { + int mx = 0; + while (x > 0) { + mx = max(mx, c[x]); + x -= x & -x; + } + return mx; + } +}; + +class Solution { +public: + int maxProfit(vector& prices, vector& profits) { + int n = prices.size(); + vector left(n, 0); + vector right(n, 0); + int m = *max_element(prices.begin(), prices.end()); + BinaryIndexedTree tree1(m + 1); + BinaryIndexedTree tree2(m + 1); + for (int i = 0; i < n; ++i) { + int x = prices[i]; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + for (int i = n - 1; i >= 0; --i) { + int x = m + 1 - prices[i]; + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + int ans = -1; + for (int i = 0; i < n; ++i) { + if (left[i] > 0 && right[i] > 0) { + ans = max(ans, left[i] + profits[i] + right[i]); + } + } + return ans; + } +}; +``` + +```cpp +class BinaryIndexedTree { +private: + int n; + vector c; + +public: + BinaryIndexedTree(int n) { + this->n = n; + c.resize(n + 1, 0); + } + + void update(int x, int v) { + while (x <= n) { + c[x] = max(c[x], v); + x += x & -x; + } + } + + int query(int x) { + int mx = 0; + while (x > 0) { + mx = max(mx, c[x]); + x -= x & -x; + } + return mx; + } +}; + +class Solution { +public: + int maxProfit(vector& prices, vector& profits) { + int n = prices.size(); + vector left(n); + vector right(n); + vector s = prices; + sort(s.begin(), s.end()); + s.erase(unique(s.begin(), s.end()), s.end()); + int m = s.size(); + BinaryIndexedTree tree1(m + 1); + BinaryIndexedTree tree2(m + 1); + for (int i = 0; i < n; ++i) { + int x = lower_bound(s.begin(), s.end(), prices[i]) - s.begin() + 1; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + for (int i = n - 1; i >= 0; --i) { + int x = m + 1 - (lower_bound(s.begin(), s.end(), prices[i]) - s.begin() + 1); + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + int ans = -1; + for (int i = 0; i < n; ++i) { + if (left[i] > 0 && right[i] > 0) { + ans = max(ans, left[i] + profits[i] + right[i]); + } + } + return ans; + } +}; +``` + ### **Go** ```go @@ -165,6 +515,138 @@ func maxProfit(prices []int, profits []int) int { } ``` +```go +type BinaryIndexedTree struct { + n int + c []int +} + +func NewBinaryIndexedTree(n int) BinaryIndexedTree { + c := make([]int, n+1) + return BinaryIndexedTree{n: n, c: c} +} + +func (bit *BinaryIndexedTree) update(x, v int) { + for x <= bit.n { + bit.c[x] = max(bit.c[x], v) + x += x & -x + } +} + +func (bit *BinaryIndexedTree) query(x int) int { + mx := 0 + for x > 0 { + mx = max(mx, bit.c[x]) + x -= x & -x + } + return mx +} + +func maxProfit(prices []int, profits []int) int { + n := len(prices) + left := make([]int, n) + right := make([]int, n) + m := 0 + + for _, x := range prices { + m = max(m, x) + } + + tree1 := NewBinaryIndexedTree(m + 1) + tree2 := NewBinaryIndexedTree(m + 1) + + for i, x := range prices { + left[i] = tree1.query(x - 1) + tree1.update(x, profits[i]) + } + + for i := n - 1; i >= 0; i-- { + x := m + 1 - prices[i] + right[i] = tree2.query(x - 1) + tree2.update(x, profits[i]) + } + + ans := -1 + + for i := 0; i < n; i++ { + if left[i] > 0 && right[i] > 0 { + ans = max(ans, left[i]+profits[i]+right[i]) + } + } + + return ans +} +``` + +```go +type BinaryIndexedTree struct { + n int + c []int +} + +func NewBinaryIndexedTree(n int) BinaryIndexedTree { + c := make([]int, n+1) + return BinaryIndexedTree{n: n, c: c} +} + +func (bit *BinaryIndexedTree) update(x, v int) { + for x <= bit.n { + bit.c[x] = max(bit.c[x], v) + x += x & -x + } +} + +func (bit *BinaryIndexedTree) query(x int) int { + mx := 0 + for x > 0 { + mx = max(mx, bit.c[x]) + x -= x & -x + } + return mx +} + +func maxProfit(prices []int, profits []int) int { + n := len(prices) + left := make([]int, n) + right := make([]int, n) + s := make([]int, n) + copy(s, prices) + sort.Ints(s) + m := 0 + for i, x := range s { + if i == 0 || x != s[i-1] { + s[m] = x + m++ + } + } + + tree1 := NewBinaryIndexedTree(m + 1) + tree2 := NewBinaryIndexedTree(m + 1) + + for i, x := range prices { + x = sort.SearchInts(s[:m], x) + 1 + left[i] = tree1.query(x - 1) + tree1.update(x, profits[i]) + } + + for i := n - 1; i >= 0; i-- { + x := m + 1 - (sort.SearchInts(s[:m], prices[i]) + 1) + right[i] = tree2.query(x - 1) + tree2.update(x, profits[i]) + } + + ans := -1 + + for i := 0; i < n; i++ { + if left[i] > 0 && right[i] > 0 { + ans = max(ans, left[i]+profits[i]+right[i]) + } + } + + return ans +} +``` + ### **TypeScript** ```ts @@ -191,6 +673,147 @@ function maxProfit(prices: number[], profits: number[]): number { } ``` +```ts +class BinaryIndexedTree { + private n: number; + private c: number[]; + + constructor(n: number) { + this.n = n; + this.c = Array(n + 1).fill(0); + } + + update(x: number, v: number): void { + while (x <= this.n) { + this.c[x] = Math.max(this.c[x], v); + x += x & -x; + } + } + + query(x: number): number { + let mx = 0; + while (x > 0) { + mx = Math.max(mx, this.c[x]); + x -= x & -x; + } + return mx; + } +} + +function maxProfit(prices: number[], profits: number[]): number { + const n: number = prices.length; + const left: number[] = Array(n).fill(0); + const right: number[] = Array(n).fill(0); + const m = Math.max(...prices); + + const tree1: BinaryIndexedTree = new BinaryIndexedTree(m + 1); + const tree2: BinaryIndexedTree = new BinaryIndexedTree(m + 1); + + for (let i = 0; i < n; i++) { + const x: number = prices[i]; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + + for (let i = n - 1; i >= 0; i--) { + const x: number = m + 1 - prices[i]; + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + + let ans: number = -1; + + for (let i = 0; i < n; i++) { + if (left[i] > 0 && right[i] > 0) { + ans = Math.max(ans, left[i] + profits[i] + right[i]); + } + } + + return ans; +} +``` + +```ts +class BinaryIndexedTree { + private n: number; + private c: number[]; + + constructor(n: number) { + this.n = n; + this.c = Array(n + 1).fill(0); + } + + update(x: number, v: number): void { + while (x <= this.n) { + this.c[x] = Math.max(this.c[x], v); + x += x & -x; + } + } + + query(x: number): number { + let mx = 0; + while (x > 0) { + mx = Math.max(mx, this.c[x]); + x -= x & -x; + } + return mx; + } +} + +function maxProfit(prices: number[], profits: number[]): number { + const n: number = prices.length; + const left: number[] = Array(n).fill(0); + const right: number[] = Array(n).fill(0); + + const s = [...prices].sort((a, b) => a - b); + let m = 0; + for (let i = 0; i < n; ++i) { + if (i === 0 || s[i] !== s[i - 1]) { + s[m++] = s[i]; + } + } + s.length = m; + + const tree1: BinaryIndexedTree = new BinaryIndexedTree(m + 1); + const tree2: BinaryIndexedTree = new BinaryIndexedTree(m + 1); + + const search = (nums: number[], x: number): number => { + let [l, r] = [0, nums.length]; + while (l < r) { + const mid = (l + r) >> 1; + if (nums[mid] >= x) { + r = mid; + } else { + l = mid + 1; + } + } + return l; + }; + + for (let i = 0; i < n; ++i) { + const x = search(s, prices[i]) + 1; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + + for (let i = n - 1; i >= 0; i--) { + const x: number = m + 1 - (search(s, prices[i]) + 1); + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + + let ans: number = -1; + + for (let i = 0; i < n; i++) { + if (left[i] > 0 && right[i] > 0) { + ans = Math.max(ans, left[i] + profits[i] + right[i]); + } + } + + return ans; +} +``` + ### **Rust** ```rust @@ -225,6 +848,73 @@ impl Solution { } ``` +```rust +struct BinaryIndexedTree { + n: usize, + c: Vec, +} + +impl BinaryIndexedTree { + fn new(n: usize) -> BinaryIndexedTree { + BinaryIndexedTree { + n, + c: vec![0; n + 1], + } + } + + fn update(&mut self, x: usize, v: i32) { + let mut x = x; + while x <= self.n { + self.c[x] = self.c[x].max(v); + x += x & x.wrapping_neg(); + } + } + + fn query(&self, x: usize) -> i32 { + let mut x = x; + let mut mx = 0; + while x > 0 { + mx = mx.max(self.c[x]); + x -= x & x.wrapping_neg(); + } + mx + } +} + +impl Solution { + pub fn max_profit(prices: Vec, profits: Vec) -> i32 { + let n = prices.len(); + let mut left = vec![0; n]; + let mut right = vec![0; n]; + let m = prices.iter().cloned().max().unwrap_or(0); + + let mut tree1 = BinaryIndexedTree::new(m as usize + 1); + let mut tree2 = BinaryIndexedTree::new(m as usize + 1); + + for i in 0..n { + let x = prices[i] as usize; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + + for i in (0..n).rev() { + let x = (m + 1 - prices[i]) as usize; + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + + let mut ans = -1; + for i in 0..n { + if left[i] > 0 && right[i] > 0 { + ans = ans.max(left[i] + profits[i] + right[i]); + } + } + + ans + } +} +``` + ### **...** ``` diff --git a/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/README.md b/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/README.md index d5ed2173b5e42..0d3aec1c85b81 100644 --- a/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/README.md +++ b/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/README.md @@ -58,6 +58,12 @@ The answer would be sum of their profits which is 5 + 4 + 6 = 15. +**方法一:树状数组** + +我们可以用两个树状数组分别维护每个价格左边以及右边的最大利润,然后枚举中间的价格,通过树状数组查询左右两边的最大利润,最后取最大值即可。 + +时间复杂度 $O(n \times \log M)$,空间复杂度 $O(M)$。其中 $n$ 是数组 $prices$ 的长度,而 $M$ 是数组 $prices$ 中的最大值,本题中 $M \le 5000$。 + ### **Python3** @@ -65,7 +71,45 @@ The answer would be sum of their profits which is 5 + 4 + 6 = 15. ```python - +class BinaryIndexedTree: + def __init__(self, n: int): + self.n = n + self.c = [0] * (n + 1) + + def update(self, x: int, v: int): + while x <= self.n: + self.c[x] = max(self.c[x], v) + x += x & -x + + def query(self, x: int) -> int: + mx = 0 + while x: + mx = max(mx, self.c[x]) + x -= x & -x + return mx + + +class Solution: + def maxProfit(self, prices: List[int], profits: List[int]) -> int: + n = len(prices) + left = [0] * n + right = [0] * n + + m = max(prices) + tree1 = BinaryIndexedTree(m + 1) + tree2 = BinaryIndexedTree(m + 1) + + for i, x in enumerate(prices): + left[i] = tree1.query(x - 1) + tree1.update(x, profits[i]) + for i in range(n - 1, -1, -1): + x = m + 1 - prices[i] + right[i] = tree2.query(x - 1) + tree2.update(x, profits[i]) + + return max( + (l + x + r for l, x, r in zip(left, profits, right) if l and r), default=-1 + ) ``` ### **Java** @@ -73,19 +117,319 @@ The answer would be sum of their profits which is 5 + 4 + 6 = 15. ```java - +class BinaryIndexedTree { + private int n; + private int[] c; + + public BinaryIndexedTree(int n) { + this.n = n; + c = new int[n + 1]; + } + + public void update(int x, int v) { + while (x <= n) { + c[x] = Math.max(c[x], v); + x += x & -x; + } + } + + public int query(int x) { + int mx = 0; + while (x > 0) { + mx = Math.max(mx, c[x]); + x -= x & -x; + } + return mx; + } +} + +class Solution { + public int maxProfit(int[] prices, int[] profits) { + int n = prices.length; + int[] left = new int[n]; + int[] right = new int[n]; + int m = 0; + for (int x : prices) { + m = Math.max(m, x); + } + BinaryIndexedTree tree1 = new BinaryIndexedTree(m + 1); + BinaryIndexedTree tree2 = new BinaryIndexedTree(m + 1); + for (int i = 0; i < n; ++i) { + int x = prices[i]; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + for (int i = n - 1; i >= 0; --i) { + int x = m + 1 - prices[i]; + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + int ans = -1; + for (int i = 0; i < n; ++i) { + if (left[i] > 0 && right[i] > 0) { + ans = Math.max(ans, left[i] + profits[i] + right[i]); + } + } + return ans; + } +} ``` ### **C++** ```cpp - +class BinaryIndexedTree { +private: + int n; + vector c; + +public: + BinaryIndexedTree(int n) { + this->n = n; + c.resize(n + 1, 0); + } + + void update(int x, int v) { + while (x <= n) { + c[x] = max(c[x], v); + x += x & -x; + } + } + + int query(int x) { + int mx = 0; + while (x > 0) { + mx = max(mx, c[x]); + x -= x & -x; + } + return mx; + } +}; + +class Solution { +public: + int maxProfit(vector& prices, vector& profits) { + int n = prices.size(); + vector left(n, 0); + vector right(n, 0); + int m = *max_element(prices.begin(), prices.end()); + BinaryIndexedTree tree1(m + 1); + BinaryIndexedTree tree2(m + 1); + for (int i = 0; i < n; ++i) { + int x = prices[i]; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + for (int i = n - 1; i >= 0; --i) { + int x = m + 1 - prices[i]; + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + int ans = -1; + for (int i = 0; i < n; ++i) { + if (left[i] > 0 && right[i] > 0) { + ans = max(ans, left[i] + profits[i] + right[i]); + } + } + return ans; + } +}; ``` ### **Go** ```go +type BinaryIndexedTree struct { + n int + c []int +} + +func NewBinaryIndexedTree(n int) BinaryIndexedTree { + c := make([]int, n+1) + return BinaryIndexedTree{n: n, c: c} +} + +func (bit *BinaryIndexedTree) update(x, v int) { + for x <= bit.n { + bit.c[x] = max(bit.c[x], v) + x += x & -x + } +} + +func (bit *BinaryIndexedTree) query(x int) int { + mx := 0 + for x > 0 { + mx = max(mx, bit.c[x]) + x -= x & -x + } + return mx +} + +func maxProfit(prices []int, profits []int) int { + n := len(prices) + left := make([]int, n) + right := make([]int, n) + m := 0 + + for _, x := range prices { + m = max(m, x) + } + + tree1 := NewBinaryIndexedTree(m + 1) + tree2 := NewBinaryIndexedTree(m + 1) + + for i, x := range prices { + left[i] = tree1.query(x - 1) + tree1.update(x, profits[i]) + } + + for i := n - 1; i >= 0; i-- { + x := m + 1 - prices[i] + right[i] = tree2.query(x - 1) + tree2.update(x, profits[i]) + } + + ans := -1 + + for i := 0; i < n; i++ { + if left[i] > 0 && right[i] > 0 { + ans = max(ans, left[i]+profits[i]+right[i]) + } + } + + return ans +} +``` + +### **TypeScript** + +```ts +class BinaryIndexedTree { + private n: number; + private c: number[]; + + constructor(n: number) { + this.n = n; + this.c = Array(n + 1).fill(0); + } + + update(x: number, v: number): void { + while (x <= this.n) { + this.c[x] = Math.max(this.c[x], v); + x += x & -x; + } + } + + query(x: number): number { + let mx = 0; + while (x > 0) { + mx = Math.max(mx, this.c[x]); + x -= x & -x; + } + return mx; + } +} + +function maxProfit(prices: number[], profits: number[]): number { + const n: number = prices.length; + const left: number[] = Array(n).fill(0); + const right: number[] = Array(n).fill(0); + const m = Math.max(...prices); + + const tree1: BinaryIndexedTree = new BinaryIndexedTree(m + 1); + const tree2: BinaryIndexedTree = new BinaryIndexedTree(m + 1); + + for (let i = 0; i < n; i++) { + const x: number = prices[i]; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + + for (let i = n - 1; i >= 0; i--) { + const x: number = m + 1 - prices[i]; + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + + let ans: number = -1; + + for (let i = 0; i < n; i++) { + if (left[i] > 0 && right[i] > 0) { + ans = Math.max(ans, left[i] + profits[i] + right[i]); + } + } + + return ans; +} +``` +### **Rust** + +```rust +struct BinaryIndexedTree { + n: usize, + c: Vec, +} + +impl BinaryIndexedTree { + fn new(n: usize) -> BinaryIndexedTree { + BinaryIndexedTree { + n, + c: vec![0; n + 1], + } + } + + fn update(&mut self, x: usize, v: i32) { + let mut x = x; + while x <= self.n { + self.c[x] = self.c[x].max(v); + x += x & x.wrapping_neg(); + } + } + + fn query(&self, x: usize) -> i32 { + let mut x = x; + let mut mx = 0; + while x > 0 { + mx = mx.max(self.c[x]); + x -= x & x.wrapping_neg(); + } + mx + } +} + +impl Solution { + pub fn max_profit(prices: Vec, profits: Vec) -> i32 { + let n = prices.len(); + let mut left = vec![0; n]; + let mut right = vec![0; n]; + let m = prices.iter().cloned().max().unwrap_or(0); + + let mut tree1 = BinaryIndexedTree::new(m as usize + 1); + let mut tree2 = BinaryIndexedTree::new(m as usize + 1); + + for i in 0..n { + let x = prices[i] as usize; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + + for i in (0..n).rev() { + let x = (m + 1 - prices[i]) as usize; + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + + let mut ans = -1; + for i in 0..n { + if left[i] > 0 && right[i] > 0 { + ans = ans.max(left[i] + profits[i] + right[i]); + } + } + + ans + } +} ``` ### **...** diff --git a/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/README_EN.md b/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/README_EN.md index 741c0fdec0783..d09ff2d936e48 100644 --- a/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/README_EN.md +++ b/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/README_EN.md @@ -54,30 +54,374 @@ The answer would be sum of their profits which is 5 + 4 + 6 = 15. ## Solutions +**Solution 1: Binary Indexed Tree** + +We can use two Binary Indexed Trees (BITs) to maintain the maximum profit on the left and right of each price, respectively. Then, we enumerate the middle price, query the maximum profit on both sides through the BIT, and finally take the maximum value. + +The time complexity is $O(n \times \log M)$, and the space complexity is $O(M)$. Here, $n$ is the length of the array $prices$, and $M$ is the maximum value in the array $prices$. In this problem, $M \le 5000$. + ### **Python3** ```python - +class BinaryIndexedTree: + def __init__(self, n: int): + self.n = n + self.c = [0] * (n + 1) + + def update(self, x: int, v: int): + while x <= self.n: + self.c[x] = max(self.c[x], v) + x += x & -x + + def query(self, x: int) -> int: + mx = 0 + while x: + mx = max(mx, self.c[x]) + x -= x & -x + return mx + + +class Solution: + def maxProfit(self, prices: List[int], profits: List[int]) -> int: + n = len(prices) + left = [0] * n + right = [0] * n + + m = max(prices) + tree1 = BinaryIndexedTree(m + 1) + tree2 = BinaryIndexedTree(m + 1) + + for i, x in enumerate(prices): + left[i] = tree1.query(x - 1) + tree1.update(x, profits[i]) + for i in range(n - 1, -1, -1): + x = m + 1 - prices[i] + right[i] = tree2.query(x - 1) + tree2.update(x, profits[i]) + + return max( + (l + x + r for l, x, r in zip(left, profits, right) if l and r), default=-1 + ) ``` ### **Java** ```java - +class BinaryIndexedTree { + private int n; + private int[] c; + + public BinaryIndexedTree(int n) { + this.n = n; + c = new int[n + 1]; + } + + public void update(int x, int v) { + while (x <= n) { + c[x] = Math.max(c[x], v); + x += x & -x; + } + } + + public int query(int x) { + int mx = 0; + while (x > 0) { + mx = Math.max(mx, c[x]); + x -= x & -x; + } + return mx; + } +} + +class Solution { + public int maxProfit(int[] prices, int[] profits) { + int n = prices.length; + int[] left = new int[n]; + int[] right = new int[n]; + int m = 0; + for (int x : prices) { + m = Math.max(m, x); + } + BinaryIndexedTree tree1 = new BinaryIndexedTree(m + 1); + BinaryIndexedTree tree2 = new BinaryIndexedTree(m + 1); + for (int i = 0; i < n; ++i) { + int x = prices[i]; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + for (int i = n - 1; i >= 0; --i) { + int x = m + 1 - prices[i]; + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + int ans = -1; + for (int i = 0; i < n; ++i) { + if (left[i] > 0 && right[i] > 0) { + ans = Math.max(ans, left[i] + profits[i] + right[i]); + } + } + return ans; + } +} ``` ### **C++** ```cpp - +class BinaryIndexedTree { +private: + int n; + vector c; + +public: + BinaryIndexedTree(int n) { + this->n = n; + c.resize(n + 1, 0); + } + + void update(int x, int v) { + while (x <= n) { + c[x] = max(c[x], v); + x += x & -x; + } + } + + int query(int x) { + int mx = 0; + while (x > 0) { + mx = max(mx, c[x]); + x -= x & -x; + } + return mx; + } +}; + +class Solution { +public: + int maxProfit(vector& prices, vector& profits) { + int n = prices.size(); + vector left(n, 0); + vector right(n, 0); + int m = *max_element(prices.begin(), prices.end()); + BinaryIndexedTree tree1(m + 1); + BinaryIndexedTree tree2(m + 1); + for (int i = 0; i < n; ++i) { + int x = prices[i]; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + for (int i = n - 1; i >= 0; --i) { + int x = m + 1 - prices[i]; + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + int ans = -1; + for (int i = 0; i < n; ++i) { + if (left[i] > 0 && right[i] > 0) { + ans = max(ans, left[i] + profits[i] + right[i]); + } + } + return ans; + } +}; ``` ### **Go** ```go +type BinaryIndexedTree struct { + n int + c []int +} + +func NewBinaryIndexedTree(n int) BinaryIndexedTree { + c := make([]int, n+1) + return BinaryIndexedTree{n: n, c: c} +} + +func (bit *BinaryIndexedTree) update(x, v int) { + for x <= bit.n { + bit.c[x] = max(bit.c[x], v) + x += x & -x + } +} + +func (bit *BinaryIndexedTree) query(x int) int { + mx := 0 + for x > 0 { + mx = max(mx, bit.c[x]) + x -= x & -x + } + return mx +} + +func maxProfit(prices []int, profits []int) int { + n := len(prices) + left := make([]int, n) + right := make([]int, n) + m := 0 + + for _, x := range prices { + m = max(m, x) + } + + tree1 := NewBinaryIndexedTree(m + 1) + tree2 := NewBinaryIndexedTree(m + 1) + + for i, x := range prices { + left[i] = tree1.query(x - 1) + tree1.update(x, profits[i]) + } + + for i := n - 1; i >= 0; i-- { + x := m + 1 - prices[i] + right[i] = tree2.query(x - 1) + tree2.update(x, profits[i]) + } + + ans := -1 + + for i := 0; i < n; i++ { + if left[i] > 0 && right[i] > 0 { + ans = max(ans, left[i]+profits[i]+right[i]) + } + } + + return ans +} +``` + +### **TypeScript** + +```ts +class BinaryIndexedTree { + private n: number; + private c: number[]; + + constructor(n: number) { + this.n = n; + this.c = Array(n + 1).fill(0); + } + + update(x: number, v: number): void { + while (x <= this.n) { + this.c[x] = Math.max(this.c[x], v); + x += x & -x; + } + } + + query(x: number): number { + let mx = 0; + while (x > 0) { + mx = Math.max(mx, this.c[x]); + x -= x & -x; + } + return mx; + } +} + +function maxProfit(prices: number[], profits: number[]): number { + const n: number = prices.length; + const left: number[] = Array(n).fill(0); + const right: number[] = Array(n).fill(0); + const m = Math.max(...prices); + + const tree1: BinaryIndexedTree = new BinaryIndexedTree(m + 1); + const tree2: BinaryIndexedTree = new BinaryIndexedTree(m + 1); + + for (let i = 0; i < n; i++) { + const x: number = prices[i]; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + + for (let i = n - 1; i >= 0; i--) { + const x: number = m + 1 - prices[i]; + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + + let ans: number = -1; + + for (let i = 0; i < n; i++) { + if (left[i] > 0 && right[i] > 0) { + ans = Math.max(ans, left[i] + profits[i] + right[i]); + } + } + + return ans; +} +``` +### **Rust** + +```rust +struct BinaryIndexedTree { + n: usize, + c: Vec, +} + +impl BinaryIndexedTree { + fn new(n: usize) -> BinaryIndexedTree { + BinaryIndexedTree { + n, + c: vec![0; n + 1], + } + } + + fn update(&mut self, x: usize, v: i32) { + let mut x = x; + while x <= self.n { + self.c[x] = self.c[x].max(v); + x += x & x.wrapping_neg(); + } + } + + fn query(&self, x: usize) -> i32 { + let mut x = x; + let mut mx = 0; + while x > 0 { + mx = mx.max(self.c[x]); + x -= x & x.wrapping_neg(); + } + mx + } +} + +impl Solution { + pub fn max_profit(prices: Vec, profits: Vec) -> i32 { + let n = prices.len(); + let mut left = vec![0; n]; + let mut right = vec![0; n]; + let m = prices.iter().cloned().max().unwrap_or(0); + + let mut tree1 = BinaryIndexedTree::new(m as usize + 1); + let mut tree2 = BinaryIndexedTree::new(m as usize + 1); + + for i in 0..n { + let x = prices[i] as usize; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + + for i in (0..n).rev() { + let x = (m + 1 - prices[i]) as usize; + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + + let mut ans = -1; + for i in 0..n { + if left[i] > 0 && right[i] > 0 { + ans = ans.max(left[i] + profits[i] + right[i]); + } + } + + ans + } +} ``` ### **...** diff --git a/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.cpp b/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.cpp new file mode 100644 index 0000000000000..0fb1d37555066 --- /dev/null +++ b/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.cpp @@ -0,0 +1,56 @@ +class BinaryIndexedTree { +private: + int n; + vector c; + +public: + BinaryIndexedTree(int n) { + this->n = n; + c.resize(n + 1, 0); + } + + void update(int x, int v) { + while (x <= n) { + c[x] = max(c[x], v); + x += x & -x; + } + } + + int query(int x) { + int mx = 0; + while (x > 0) { + mx = max(mx, c[x]); + x -= x & -x; + } + return mx; + } +}; + +class Solution { +public: + int maxProfit(vector& prices, vector& profits) { + int n = prices.size(); + vector left(n, 0); + vector right(n, 0); + int m = *max_element(prices.begin(), prices.end()); + BinaryIndexedTree tree1(m + 1); + BinaryIndexedTree tree2(m + 1); + for (int i = 0; i < n; ++i) { + int x = prices[i]; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + for (int i = n - 1; i >= 0; --i) { + int x = m + 1 - prices[i]; + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + int ans = -1; + for (int i = 0; i < n; ++i) { + if (left[i] > 0 && right[i] > 0) { + ans = max(ans, left[i] + profits[i] + right[i]); + } + } + return ans; + } +}; diff --git a/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.go b/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.go new file mode 100644 index 0000000000000..fb35aff995139 --- /dev/null +++ b/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.go @@ -0,0 +1,60 @@ +type BinaryIndexedTree struct { + n int + c []int +} + +func NewBinaryIndexedTree(n int) BinaryIndexedTree { + c := make([]int, n+1) + return BinaryIndexedTree{n: n, c: c} +} + +func (bit *BinaryIndexedTree) update(x, v int) { + for x <= bit.n { + bit.c[x] = max(bit.c[x], v) + x += x & -x + } +} + +func (bit *BinaryIndexedTree) query(x int) int { + mx := 0 + for x > 0 { + mx = max(mx, bit.c[x]) + x -= x & -x + } + return mx +} + +func maxProfit(prices []int, profits []int) int { + n := len(prices) + left := make([]int, n) + right := make([]int, n) + m := 0 + + for _, x := range prices { + m = max(m, x) + } + + tree1 := NewBinaryIndexedTree(m + 1) + tree2 := NewBinaryIndexedTree(m + 1) + + for i, x := range prices { + left[i] = tree1.query(x - 1) + tree1.update(x, profits[i]) + } + + for i := n - 1; i >= 0; i-- { + x := m + 1 - prices[i] + right[i] = tree2.query(x - 1) + tree2.update(x, profits[i]) + } + + ans := -1 + + for i := 0; i < n; i++ { + if left[i] > 0 && right[i] > 0 { + ans = max(ans, left[i]+profits[i]+right[i]) + } + } + + return ans +} \ No newline at end of file diff --git a/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.java b/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.java new file mode 100644 index 0000000000000..816810b673ea1 --- /dev/null +++ b/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.java @@ -0,0 +1,56 @@ +class BinaryIndexedTree { + private int n; + private int[] c; + + public BinaryIndexedTree(int n) { + this.n = n; + c = new int[n + 1]; + } + + public void update(int x, int v) { + while (x <= n) { + c[x] = Math.max(c[x], v); + x += x & -x; + } + } + + public int query(int x) { + int mx = 0; + while (x > 0) { + mx = Math.max(mx, c[x]); + x -= x & -x; + } + return mx; + } +} + +class Solution { + public int maxProfit(int[] prices, int[] profits) { + int n = prices.length; + int[] left = new int[n]; + int[] right = new int[n]; + int m = 0; + for (int x : prices) { + m = Math.max(m, x); + } + BinaryIndexedTree tree1 = new BinaryIndexedTree(m + 1); + BinaryIndexedTree tree2 = new BinaryIndexedTree(m + 1); + for (int i = 0; i < n; ++i) { + int x = prices[i]; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + for (int i = n - 1; i >= 0; --i) { + int x = m + 1 - prices[i]; + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + int ans = -1; + for (int i = 0; i < n; ++i) { + if (left[i] > 0 && right[i] > 0) { + ans = Math.max(ans, left[i] + profits[i] + right[i]); + } + } + return ans; + } +} \ No newline at end of file diff --git a/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.py b/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.py new file mode 100644 index 0000000000000..6ab10306a6ecd --- /dev/null +++ b/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.py @@ -0,0 +1,39 @@ +class BinaryIndexedTree: + def __init__(self, n: int): + self.n = n + self.c = [0] * (n + 1) + + def update(self, x: int, v: int): + while x <= self.n: + self.c[x] = max(self.c[x], v) + x += x & -x + + def query(self, x: int) -> int: + mx = 0 + while x: + mx = max(mx, self.c[x]) + x -= x & -x + return mx + + +class Solution: + def maxProfit(self, prices: List[int], profits: List[int]) -> int: + n = len(prices) + left = [0] * n + right = [0] * n + + m = max(prices) + tree1 = BinaryIndexedTree(m + 1) + tree2 = BinaryIndexedTree(m + 1) + + for i, x in enumerate(prices): + left[i] = tree1.query(x - 1) + tree1.update(x, profits[i]) + for i in range(n - 1, -1, -1): + x = m + 1 - prices[i] + right[i] = tree2.query(x - 1) + tree2.update(x, profits[i]) + + return max( + (l + x + r for l, x, r in zip(left, profits, right) if l and r), default=-1 + ) diff --git a/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.rs b/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.rs new file mode 100644 index 0000000000000..d1244b86e8b21 --- /dev/null +++ b/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.rs @@ -0,0 +1,64 @@ +struct BinaryIndexedTree { + n: usize, + c: Vec, +} + +impl BinaryIndexedTree { + fn new(n: usize) -> BinaryIndexedTree { + BinaryIndexedTree { + n, + c: vec![0; n + 1], + } + } + + fn update(&mut self, x: usize, v: i32) { + let mut x = x; + while x <= self.n { + self.c[x] = self.c[x].max(v); + x += x & x.wrapping_neg(); + } + } + + fn query(&self, x: usize) -> i32 { + let mut x = x; + let mut mx = 0; + while x > 0 { + mx = mx.max(self.c[x]); + x -= x & x.wrapping_neg(); + } + mx + } +} + +impl Solution { + pub fn max_profit(prices: Vec, profits: Vec) -> i32 { + let n = prices.len(); + let mut left = vec![0; n]; + let mut right = vec![0; n]; + let m = prices.iter().cloned().max().unwrap_or(0); + + let mut tree1 = BinaryIndexedTree::new(m as usize + 1); + let mut tree2 = BinaryIndexedTree::new(m as usize + 1); + + for i in 0..n { + let x = prices[i] as usize; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + + for i in (0..n).rev() { + let x = (m + 1 - prices[i]) as usize; + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + + let mut ans = -1; + for i in 0..n { + if left[i] > 0 && right[i] > 0 { + ans = ans.max(left[i] + profits[i] + right[i]); + } + } + + ans + } +} \ No newline at end of file diff --git a/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.ts b/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.ts new file mode 100644 index 0000000000000..d719547a7186b --- /dev/null +++ b/solution/2900-2999/2921.Maximum Profitable Triplets With Increasing Prices II/Solution.ts @@ -0,0 +1,57 @@ +class BinaryIndexedTree { + private n: number; + private c: number[]; + + constructor(n: number) { + this.n = n; + this.c = Array(n + 1).fill(0); + } + + update(x: number, v: number): void { + while (x <= this.n) { + this.c[x] = Math.max(this.c[x], v); + x += x & -x; + } + } + + query(x: number): number { + let mx = 0; + while (x > 0) { + mx = Math.max(mx, this.c[x]); + x -= x & -x; + } + return mx; + } +} + +function maxProfit(prices: number[], profits: number[]): number { + const n: number = prices.length; + const left: number[] = Array(n).fill(0); + const right: number[] = Array(n).fill(0); + const m = Math.max(...prices); + + const tree1: BinaryIndexedTree = new BinaryIndexedTree(m + 1); + const tree2: BinaryIndexedTree = new BinaryIndexedTree(m + 1); + + for (let i = 0; i < n; i++) { + const x: number = prices[i]; + left[i] = tree1.query(x - 1); + tree1.update(x, profits[i]); + } + + for (let i = n - 1; i >= 0; i--) { + const x: number = m + 1 - prices[i]; + right[i] = tree2.query(x - 1); + tree2.update(x, profits[i]); + } + + let ans: number = -1; + + for (let i = 0; i < n; i++) { + if (left[i] > 0 && right[i] > 0) { + ans = Math.max(ans, left[i] + profits[i] + right[i]); + } + } + + return ans; +}