From bdca2c8d676c1af7d623482bf4a5eda3b97fbe43 Mon Sep 17 00:00:00 2001 From: yanglbme Date: Sun, 22 Jun 2025 20:46:45 +0800 Subject: [PATCH] feat: add solutions to lc problem: No.1751 No.1751.Maximum Number of Events That Can Be Attended II --- .../README.md | 107 ++++++++++----- .../README_EN.md | 128 ++++++++++++++---- .../Solution.cpp | 14 +- .../Solution.ts | 46 ++++--- .../Solution2.ts | 26 ++++ 5 files changed, 243 insertions(+), 78 deletions(-) create mode 100644 solution/1700-1799/1751.Maximum Number of Events That Can Be Attended II/Solution2.ts diff --git a/solution/1700-1799/1751.Maximum Number of Events That Can Be Attended II/README.md b/solution/1700-1799/1751.Maximum Number of Events That Can Be Attended II/README.md index 97bd26ee00b3a..0ae985fff2bd2 100644 --- a/solution/1700-1799/1751.Maximum Number of Events That Can Be Attended II/README.md +++ b/solution/1700-1799/1751.Maximum Number of Events That Can Be Attended II/README.md @@ -76,21 +76,21 @@ tags: ### 方法一:记忆化搜索 + 二分查找 -我们先将会议按照开始时间从小到大排序,然后设计一个函数 $dfs(i, k)$ 表示从第 $i$ 个会议开始,最多参加 $k$ 个会议的最大价值和。答案即为 $dfs(0, k)$。 +我们先将会议按照开始时间从小到大排序,然后设计一个函数 $\text{dfs}(i, k)$ 表示从第 $i$ 个会议开始,最多参加 $k$ 个会议的最大价值和。答案即为 $\text{dfs}(0, k)$。 -函数 $dfs(i, k)$ 的计算过程如下: +函数 $\text{dfs}(i, k)$ 的计算过程如下: -对于第 $i$ 个会议,我们可以选择参加或者不参加。如果不参加,那么最大价值和就是 $dfs(i + 1, k)$,如果参加,我们可以通过二分查找,找到第一个开始时间大于第 $i$ 个会议结束时间的会议,记为 $j$,那么最大价值和就是 $dfs(j, k - 1) + value[i]$。取二者的较大值即可。即: +如果不参加第 $i$ 个会议,那么最大价值和就是 $\text{dfs}(i + 1, k)$;如果参加第 $i$ 个会议,我们可以通过二分查找,找到第一个开始时间大于第 $i$ 个会议结束时间的会议,记为 $j$,那么最大价值和就是 $\text{dfs}(j, k - 1) + \text{value}[i]$。取二者的较大值即可。即: $$ -dfs(i, k) = \max(dfs(i + 1, k), dfs(j, k - 1) + value[i]) +\text{dfs}(i, k) = \max(\text{dfs}(i + 1, k), \text{dfs}(j, k - 1) + \text{value}[i]) $$ 其中 $j$ 为第一个开始时间大于第 $i$ 个会议结束时间的会议,可以通过二分查找得到。 -由于函数 $dfs(i, k)$ 的计算过程中,会调用 $dfs(i + 1, k)$ 和 $dfs(j, k - 1)$,因此我们可以使用记忆化搜索,将计算过的值保存下来,避免重复计算。 +由于函数 $\text{dfs}(i, k)$ 的计算过程中,会调用 $\text{dfs}(i + 1, k)$ 和 $\text{dfs}(j, k - 1)$,因此我们可以使用记忆化搜索,将计算过的值保存下来,避免重复计算。 -时间复杂度 $O(n\times \log n + n\times k)$,其中 $n$ 为会议数量。 +时间复杂度 $O(n \times \log n + n \times k)$,空间复杂度 $O(n \times k)$,其中 $n$ 为会议数量。 @@ -163,23 +163,29 @@ class Solution { class Solution { public: int maxValue(vector>& events, int k) { - sort(events.begin(), events.end()); + ranges::sort(events); int n = events.size(); int f[n][k + 1]; memset(f, 0, sizeof(f)); - function dfs = [&](int i, int k) -> int { + auto dfs = [&](this auto&& dfs, int i, int k) -> int { if (i >= n || k <= 0) { return 0; } if (f[i][k] > 0) { return f[i][k]; } + int ed = events[i][1], val = events[i][2]; vector t = {ed}; - int p = upper_bound(events.begin() + i + 1, events.end(), t, [](const auto& a, const auto& b) { return a[0] < b[0]; }) - events.begin(); + + int p = upper_bound(events.begin() + i + 1, events.end(), t, + [](const auto& a, const auto& b) { return a[0] < b[0]; }) + - events.begin(); + f[i][k] = max(dfs(i + 1, k), dfs(p, k - 1) + val); return f[i][k]; }; + return dfs(0, k); } }; @@ -216,30 +222,38 @@ func maxValue(events [][]int, k int) int { ```ts function maxValue(events: number[][], k: number): number { - events.sort((a, b) => a[1] - b[1]); + events.sort((a, b) => a[0] - b[0]); const n = events.length; - const f: number[][] = new Array(n + 1).fill(0).map(() => new Array(k + 1).fill(0)); - const search = (x: number, hi: number): number => { - let l = 0; - let r = hi; - while (l < r) { - const mid = (l + r) >> 1; - if (events[mid][1] >= x) { - r = mid; + const f: number[][] = Array.from({ length: n }, () => Array(k + 1).fill(0)); + + const dfs = (i: number, k: number): number => { + if (i >= n || k <= 0) { + return 0; + } + if (f[i][k] > 0) { + return f[i][k]; + } + + const ed = events[i][1], + val = events[i][2]; + + let left = i + 1, + right = n; + while (left < right) { + const mid = (left + right) >> 1; + if (events[mid][0] > ed) { + right = mid; } else { - l = mid + 1; + left = mid + 1; } } - return l; + const p = left; + + f[i][k] = Math.max(dfs(i + 1, k), dfs(p, k - 1) + val); + return f[i][k]; }; - for (let i = 1; i <= n; ++i) { - const [st, _, val] = events[i - 1]; - const p = search(st, i - 1); - for (let j = 1; j <= k; ++j) { - f[i][j] = Math.max(f[i - 1][j], f[p][j - 1] + val); - } - } - return f[n][k]; + + return dfs(0, k); } ``` @@ -255,15 +269,15 @@ function maxValue(events: number[][], k: number): number { 先将会议排序,这次我们按照结束时间从小到大排序。然后定义 $f[i][j]$ 表示前 $i$ 个会议中,最多参加 $j$ 个会议的最大价值和。答案即为 $f[n][k]$。 -对于第 $i$ 个会议,我们可以选择参加或者不参加。如果不参加,那么最大价值和就是 $f[i][j]$,如果参加,我们可以通过二分查找,找到最后一个结束时间小于第 $i$ 个会议开始时间的会议,记为 $h$,那么最大价值和就是 $f[h+1][j - 1] + value[i]$。取二者的较大值即可。即: +对于第 $i$ 个会议,我们可以选择参加或者不参加。如果不参加,那么最大价值和就是 $f[i][j]$,如果参加,我们可以通过二分查找,找到最后一个结束时间小于第 $i$ 个会议开始时间的会议,记为 $h$,那么最大价值和就是 $f[h + 1][j - 1] + \text{value}[i]$。取二者的较大值即可。即: $$ -f[i+1][j] = \max(f[i][j], f[h+1][j - 1] + value[i]) +f[i + 1][j] = \max(f[i][j], f[h + 1][j - 1] + \text{value}[i]) $$ 其中 $h$ 为最后一个结束时间小于第 $i$ 个会议开始时间的会议,可以通过二分查找得到。 -时间复杂度 $O(n\times \log n + n\times k)$,其中 $n$ 为会议数量。 +时间复杂度 $O(n \times \log n + n \times k)$,空间复杂度 $O(n \times k)$,其中 $n$ 为会议数量。 相似题目: @@ -364,6 +378,37 @@ func maxValue(events [][]int, k int) int { } ``` +#### TypeScript + +```ts +function maxValue(events: number[][], k: number): number { + events.sort((a, b) => a[1] - b[1]); + const n = events.length; + const f: number[][] = new Array(n + 1).fill(0).map(() => new Array(k + 1).fill(0)); + const search = (x: number, hi: number): number => { + let l = 0; + let r = hi; + while (l < r) { + const mid = (l + r) >> 1; + if (events[mid][1] >= x) { + r = mid; + } else { + l = mid + 1; + } + } + return l; + }; + for (let i = 1; i <= n; ++i) { + const [st, _, val] = events[i - 1]; + const p = search(st, i - 1); + for (let j = 1; j <= k; ++j) { + f[i][j] = Math.max(f[i - 1][j], f[p][j - 1] + val); + } + } + return f[n][k]; +} +``` + diff --git a/solution/1700-1799/1751.Maximum Number of Events That Can Be Attended II/README_EN.md b/solution/1700-1799/1751.Maximum Number of Events That Can Be Attended II/README_EN.md index a8a515dca126a..571eacd071403 100644 --- a/solution/1700-1799/1751.Maximum Number of Events That Can Be Attended II/README_EN.md +++ b/solution/1700-1799/1751.Maximum Number of Events That Can Be Attended II/README_EN.md @@ -72,7 +72,23 @@ Notice that you cannot attend any other event as they overlap, and that you do < -### Solution 1 +### Solution 1: Memoization + Binary Search + +First, we sort the events by their start time in ascending order. Then, we define a function $\text{dfs}(i, k)$, which represents the maximum total value achievable by attending at most $k$ events starting from the $i$-th event. The answer is $\text{dfs}(0, k)$. + +The calculation process of the function $\text{dfs}(i, k)$ is as follows: + +If we do not attend the $i$-th event, the maximum value is $\text{dfs}(i + 1, k)$. If we attend the $i$-th event, we can use binary search to find the first event whose start time is greater than the end time of the $i$-th event, denoted as $j$. Then, the maximum value is $\text{dfs}(j, k - 1) + \text{value}[i]$. We take the maximum of the two options: + +$$ +\text{dfs}(i, k) = \max(\text{dfs}(i + 1, k), \text{dfs}(j, k - 1) + \text{value}[i]) +$$ + +Here, $j$ is the index of the first event whose start time is greater than the end time of the $i$-th event, which can be found using binary search. + +Since the calculation of $\text{dfs}(i, k)$ involves calls to $\text{dfs}(i + 1, k)$ and $\text{dfs}(j, k - 1)$, we can use memoization to store the computed values and avoid redundant calculations. + +The time complexity is $O(n \times \log n + n \times k)$, and the space complexity is $O(n \times k)$, where $n$ is the number of events. @@ -145,23 +161,29 @@ class Solution { class Solution { public: int maxValue(vector>& events, int k) { - sort(events.begin(), events.end()); + ranges::sort(events); int n = events.size(); int f[n][k + 1]; memset(f, 0, sizeof(f)); - function dfs = [&](int i, int k) -> int { + auto dfs = [&](this auto&& dfs, int i, int k) -> int { if (i >= n || k <= 0) { return 0; } if (f[i][k] > 0) { return f[i][k]; } + int ed = events[i][1], val = events[i][2]; vector t = {ed}; - int p = upper_bound(events.begin() + i + 1, events.end(), t, [](const auto& a, const auto& b) { return a[0] < b[0]; }) - events.begin(); + + int p = upper_bound(events.begin() + i + 1, events.end(), t, + [](const auto& a, const auto& b) { return a[0] < b[0]; }) + - events.begin(); + f[i][k] = max(dfs(i + 1, k), dfs(p, k - 1) + val); return f[i][k]; }; + return dfs(0, k); } }; @@ -198,30 +220,38 @@ func maxValue(events [][]int, k int) int { ```ts function maxValue(events: number[][], k: number): number { - events.sort((a, b) => a[1] - b[1]); + events.sort((a, b) => a[0] - b[0]); const n = events.length; - const f: number[][] = new Array(n + 1).fill(0).map(() => new Array(k + 1).fill(0)); - const search = (x: number, hi: number): number => { - let l = 0; - let r = hi; - while (l < r) { - const mid = (l + r) >> 1; - if (events[mid][1] >= x) { - r = mid; + const f: number[][] = Array.from({ length: n }, () => Array(k + 1).fill(0)); + + const dfs = (i: number, k: number): number => { + if (i >= n || k <= 0) { + return 0; + } + if (f[i][k] > 0) { + return f[i][k]; + } + + const ed = events[i][1], + val = events[i][2]; + + let left = i + 1, + right = n; + while (left < right) { + const mid = (left + right) >> 1; + if (events[mid][0] > ed) { + right = mid; } else { - l = mid + 1; + left = mid + 1; } } - return l; + const p = left; + + f[i][k] = Math.max(dfs(i + 1, k), dfs(p, k - 1) + val); + return f[i][k]; }; - for (let i = 1; i <= n; ++i) { - const [st, _, val] = events[i - 1]; - const p = search(st, i - 1); - for (let j = 1; j <= k; ++j) { - f[i][j] = Math.max(f[i - 1][j], f[p][j - 1] + val); - } - } - return f[n][k]; + + return dfs(0, k); } ``` @@ -231,7 +261,26 @@ function maxValue(events: number[][], k: number): number { -### Solution 2 +### Solution 2: Dynamic Programming + Binary Search + +We can convert the memoization approach in Solution 1 to dynamic programming. + +First, sort the events, this time by end time in ascending order. Then define $f[i][j]$ as the maximum total value by attending at most $j$ events among the first $i$ events. The answer is $f[n][k]$. + +For the $i$-th event, we can choose to attend it or not. If we do not attend, the maximum value is $f[i][j]$. If we attend, we can use binary search to find the last event whose end time is less than the start time of the $i$-th event, denoted as $h$. Then the maximum value is $f[h + 1][j - 1] + \text{value}[i]$. We take the maximum of the two options: + +$$ +f[i + 1][j] = \max(f[i][j], f[h + 1][j - 1] + \text{value}[i]) +$$ + +Here, $h$ is the last event whose end time is less than the start time of the $i$-th event, which can be found by binary search. + +The time complexity is $O(n \times \log n + n \times k)$, and the space complexity is $O(n \times k)$, where $n$ is the number of events. + +Related problems: + +- [1235. Maximum Profit in Job Scheduling](https://github.com/doocs/leetcode/blob/main/solution/1200-1299/1235.Maximum%20Profit%20in%20Job%20Scheduling/README_EN.md) +- [2008. Maximum Earnings From Taxi](https://github.com/doocs/leetcode/blob/main/solution/2000-2099/2008.Maximum%20Earnings%20From%20Taxi/README_EN.md) @@ -327,6 +376,37 @@ func maxValue(events [][]int, k int) int { } ``` +#### TypeScript + +```ts +function maxValue(events: number[][], k: number): number { + events.sort((a, b) => a[1] - b[1]); + const n = events.length; + const f: number[][] = new Array(n + 1).fill(0).map(() => new Array(k + 1).fill(0)); + const search = (x: number, hi: number): number => { + let l = 0; + let r = hi; + while (l < r) { + const mid = (l + r) >> 1; + if (events[mid][1] >= x) { + r = mid; + } else { + l = mid + 1; + } + } + return l; + }; + for (let i = 1; i <= n; ++i) { + const [st, _, val] = events[i - 1]; + const p = search(st, i - 1); + for (let j = 1; j <= k; ++j) { + f[i][j] = Math.max(f[i - 1][j], f[p][j - 1] + val); + } + } + return f[n][k]; +} +``` + diff --git a/solution/1700-1799/1751.Maximum Number of Events That Can Be Attended II/Solution.cpp b/solution/1700-1799/1751.Maximum Number of Events That Can Be Attended II/Solution.cpp index 42e112883fae4..c854e716cef7b 100644 --- a/solution/1700-1799/1751.Maximum Number of Events That Can Be Attended II/Solution.cpp +++ b/solution/1700-1799/1751.Maximum Number of Events That Can Be Attended II/Solution.cpp @@ -1,23 +1,29 @@ class Solution { public: int maxValue(vector>& events, int k) { - sort(events.begin(), events.end()); + ranges::sort(events); int n = events.size(); int f[n][k + 1]; memset(f, 0, sizeof(f)); - function dfs = [&](int i, int k) -> int { + auto dfs = [&](this auto&& dfs, int i, int k) -> int { if (i >= n || k <= 0) { return 0; } if (f[i][k] > 0) { return f[i][k]; } + int ed = events[i][1], val = events[i][2]; vector t = {ed}; - int p = upper_bound(events.begin() + i + 1, events.end(), t, [](const auto& a, const auto& b) { return a[0] < b[0]; }) - events.begin(); + + int p = upper_bound(events.begin() + i + 1, events.end(), t, + [](const auto& a, const auto& b) { return a[0] < b[0]; }) + - events.begin(); + f[i][k] = max(dfs(i + 1, k), dfs(p, k - 1) + val); return f[i][k]; }; + return dfs(0, k); } -}; \ No newline at end of file +}; diff --git a/solution/1700-1799/1751.Maximum Number of Events That Can Be Attended II/Solution.ts b/solution/1700-1799/1751.Maximum Number of Events That Can Be Attended II/Solution.ts index c370b2cb5e927..169bcdab7d110 100644 --- a/solution/1700-1799/1751.Maximum Number of Events That Can Be Attended II/Solution.ts +++ b/solution/1700-1799/1751.Maximum Number of Events That Can Be Attended II/Solution.ts @@ -1,26 +1,34 @@ function maxValue(events: number[][], k: number): number { - events.sort((a, b) => a[1] - b[1]); + events.sort((a, b) => a[0] - b[0]); const n = events.length; - const f: number[][] = new Array(n + 1).fill(0).map(() => new Array(k + 1).fill(0)); - const search = (x: number, hi: number): number => { - let l = 0; - let r = hi; - while (l < r) { - const mid = (l + r) >> 1; - if (events[mid][1] >= x) { - r = mid; + const f: number[][] = Array.from({ length: n }, () => Array(k + 1).fill(0)); + + const dfs = (i: number, k: number): number => { + if (i >= n || k <= 0) { + return 0; + } + if (f[i][k] > 0) { + return f[i][k]; + } + + const ed = events[i][1], + val = events[i][2]; + + let left = i + 1, + right = n; + while (left < right) { + const mid = (left + right) >> 1; + if (events[mid][0] > ed) { + right = mid; } else { - l = mid + 1; + left = mid + 1; } } - return l; + const p = left; + + f[i][k] = Math.max(dfs(i + 1, k), dfs(p, k - 1) + val); + return f[i][k]; }; - for (let i = 1; i <= n; ++i) { - const [st, _, val] = events[i - 1]; - const p = search(st, i - 1); - for (let j = 1; j <= k; ++j) { - f[i][j] = Math.max(f[i - 1][j], f[p][j - 1] + val); - } - } - return f[n][k]; + + return dfs(0, k); } diff --git a/solution/1700-1799/1751.Maximum Number of Events That Can Be Attended II/Solution2.ts b/solution/1700-1799/1751.Maximum Number of Events That Can Be Attended II/Solution2.ts new file mode 100644 index 0000000000000..c370b2cb5e927 --- /dev/null +++ b/solution/1700-1799/1751.Maximum Number of Events That Can Be Attended II/Solution2.ts @@ -0,0 +1,26 @@ +function maxValue(events: number[][], k: number): number { + events.sort((a, b) => a[1] - b[1]); + const n = events.length; + const f: number[][] = new Array(n + 1).fill(0).map(() => new Array(k + 1).fill(0)); + const search = (x: number, hi: number): number => { + let l = 0; + let r = hi; + while (l < r) { + const mid = (l + r) >> 1; + if (events[mid][1] >= x) { + r = mid; + } else { + l = mid + 1; + } + } + return l; + }; + for (let i = 1; i <= n; ++i) { + const [st, _, val] = events[i - 1]; + const p = search(st, i - 1); + for (let j = 1; j <= k; ++j) { + f[i][j] = Math.max(f[i - 1][j], f[p][j - 1] + val); + } + } + return f[n][k]; +}