diff --git a/Assets/Origins/README-Head.md b/Assets/Origins/README-Head.md index 9fd50c59..d1b24470 100644 --- a/Assets/Origins/README-Head.md +++ b/Assets/Origins/README-Head.md @@ -2,7 +2,7 @@ ## 项目简介 -- **「算法与数据结构」** 基础知识的讲解教程,「LeetCode」750+ 道题目的详细解析。本项目易于理解,没有大跨度的思维跳跃,项目中使用部分图示、例子来帮助理解。 +- **「算法与数据结构」** 基础知识的讲解教程,「LeetCode」800+ 道题目的详细解析。本项目易于理解,没有大跨度的思维跳跃,项目中使用部分图示、例子来帮助理解。 - 本教程先从基础的数据结构和算法开始讲解,再针对不同分类的数据结构和算法,进行具体题目的讲解分析。让读者可以通过「算法基础理论学习」和「编程实战学习」相结合的方式,彻底的掌握算法知识。 @@ -26,7 +26,7 @@ 我是一名 iOS / macOS 的开发程序员,另外也是北航软院的一名非全硕士(在读)。曾在大学期间学习过算法知识,并参加过 3 年的 ACM 比赛, 但水平有限,未能取得理想成绩。但是这 3 年的 ACM 经历,给我最大的收获是锻炼了自己的逻辑思维和解决实际问题的能力,这种能力为我今后的工作、学习打下了坚实的基础。 -我从 2021 年 03 月 30 日开始每日在 LeetCode 刷题,到 2022 年 06 月 08 日已经刷了 1000+ 道题目,并且完成了 750+ 道题解。努力向着 1000+、1500+、2000+ 道题解前进。 +我从 2021 年 03 月 30 日开始每日在 LeetCode 刷题,到 2022 年 06 月 08 日已经刷了 1000+ 道题目,并且完成了 800+ 道题解。努力向着 1000+、1500+、2000+ 道题解前进。 在公众号 **「程序员充电站」** 里回复 "**算法打卡**",拉你进 LeetCode 算法打卡计划群一起组队打卡。 diff --git a/Assets/Origins/Root-Index-Head.md b/Assets/Origins/Root-Index-Head.md index 71f4dc4f..692dcfc1 100644 --- a/Assets/Origins/Root-Index-Head.md +++ b/Assets/Origins/Root-Index-Head.md @@ -38,7 +38,7 @@ **学习数据结构与算法的关键,在于掌握其中的思想和精髓,学会解决实际问题的方法。** -本书采用算法与数据结构相结合的方法,把内容分为如下 `6` 部分: +本书采用算法与数据结构相结合的方法,把内容分为如下 6 部分: - 第一部分是序言(第 00 章):介绍数据结构与算法的基础知识、算法复杂度、LeetCode 的入门和攻略,为后面的学习打好基础。 - 第二部分是数据结构篇(第 01 ~ 08 章):每一章对应一种数据结构,这个部分用来介绍最常见、最重要的数据结构,以及与该数据结构相关的算法知识。 @@ -69,7 +69,7 @@ 我是一名 iOS / macOS 的开发程序员,另外也是北航软院的一名非全硕士(在读)。曾在大学期间学习过算法知识,并参加过 3 年的 ACM 比赛, 但水平有限,未能取得理想成绩。但是这 3 年的 ACM 经历,给我最大的收获是锻炼了自己的逻辑思维和解决实际问题的能力,这种能力为我今后的工作、学习打下了坚实的基础。 -我从 2021 年 03 月 30 日开始每日在 LeetCode 刷题,到 2022 年 06 月 08 日已经刷了 1000+ 道题目,并且完成了 750+ 道题解。努力向着 1000+、1500+、2000+ 道题解前进。 +我从 2021 年 03 月 30 日开始每日在 LeetCode 刷题,到 2022 年 06 月 08 日已经刷了 1000+ 道题目,并且完成了 800+ 道题解。努力向着 1000+、1500+、2000+ 道题解前进。 ## 版权说明 diff --git a/Contents/00.Introduction/04.Solutions-List.md b/Contents/00.Introduction/04.Solutions-List.md index db762051..ebdd36b7 100644 --- a/Contents/00.Introduction/04.Solutions-List.md +++ b/Contents/00.Introduction/04.Solutions-List.md @@ -1,4 +1,4 @@ -# LeetCode 题解(已完成 799 道) +# LeetCode 题解(已完成 801 道) | 题号 | 标题 | 题解 | 标签 | 难度 | | :------ | :------ | :------ | :------ | :------ | @@ -518,6 +518,7 @@ | 1137 | [第 N 个泰波那契数](https://leetcode.cn/problems/n-th-tribonacci-number/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/1137.%20%E7%AC%AC%20N%20%E4%B8%AA%E6%B3%B0%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0.md) | 记忆化搜索、数学、动态规划 | 简单 | | 1143 | [最长公共子序列](https://leetcode.cn/problems/longest-common-subsequence/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/1143.%20%E6%9C%80%E9%95%BF%E5%85%AC%E5%85%B1%E5%AD%90%E5%BA%8F%E5%88%97.md) | 字符串、动态规划 | 中等 | | 1151 | [最少交换次数来组合所有的 1](https://leetcode.cn/problems/minimum-swaps-to-group-all-1s-together/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/1151.%20%E6%9C%80%E5%B0%91%E4%BA%A4%E6%8D%A2%E6%AC%A1%E6%95%B0%E6%9D%A5%E7%BB%84%E5%90%88%E6%89%80%E6%9C%89%E7%9A%84%201.md) | 数组、滑动窗口 | 中等 | +| 1155 | [掷骰子等于目标和的方法数](https://leetcode.cn/problems/number-of-dice-rolls-with-target-sum/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/1155.%20%E6%8E%B7%E9%AA%B0%E5%AD%90%E7%AD%89%E4%BA%8E%E7%9B%AE%E6%A0%87%E5%92%8C%E7%9A%84%E6%96%B9%E6%B3%95%E6%95%B0.md) | 动态规划 | 中等 | | 1161 | [最大层内元素和](https://leetcode.cn/problems/maximum-level-sum-of-a-binary-tree/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/1161.%20%E6%9C%80%E5%A4%A7%E5%B1%82%E5%86%85%E5%85%83%E7%B4%A0%E5%92%8C.md) | 树、深度优先搜索、广度优先搜索、二叉树 | 中等 | | 1176 | [健身计划评估](https://leetcode.cn/problems/diet-plan-performance/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/1176.%20%E5%81%A5%E8%BA%AB%E8%AE%A1%E5%88%92%E8%AF%84%E4%BC%B0.md) | 数组、滑动窗口 | 简单 | | 1184 | [公交站间的距离](https://leetcode.cn/problems/distance-between-bus-stops/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/1184.%20%E5%85%AC%E4%BA%A4%E7%AB%99%E9%97%B4%E7%9A%84%E8%B7%9D%E7%A6%BB.md) | 数组 | 简单 | @@ -600,6 +601,7 @@ | 2376 | [统计特殊整数](https://leetcode.cn/problems/count-special-integers/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/2376.%20%E7%BB%9F%E8%AE%A1%E7%89%B9%E6%AE%8A%E6%95%B4%E6%95%B0.md) | 数学、动态规划 | 困难 | | 2427 | [公因子的数目](https://leetcode.cn/problems/number-of-common-factors/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/2427.%20%E5%85%AC%E5%9B%A0%E5%AD%90%E7%9A%84%E6%95%B0%E7%9B%AE.md) | 数学、枚举、数论 | 简单 | | 2538 | [最大价值和与最小价值和的差值](https://leetcode.cn/problems/difference-between-maximum-and-minimum-price-sum/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/2538.%20%E6%9C%80%E5%A4%A7%E4%BB%B7%E5%80%BC%E5%92%8C%E4%B8%8E%E6%9C%80%E5%B0%8F%E4%BB%B7%E5%80%BC%E5%92%8C%E7%9A%84%E5%B7%AE%E5%80%BC.md) | 树、深度优先搜索、数组、动态规划 | 困难 | +| 2585 | [获得分数的方法数](https://leetcode.cn/problems/number-of-ways-to-earn-points/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/2585.%20%E8%8E%B7%E5%BE%97%E5%88%86%E6%95%B0%E7%9A%84%E6%96%B9%E6%B3%95%E6%95%B0.md) | 数组、动态规划 | 困难 | | 2719 | [统计整数数目](https://leetcode.cn/problems/count-of-integers/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/2719.%20%E7%BB%9F%E8%AE%A1%E6%95%B4%E6%95%B0%E6%95%B0%E7%9B%AE.md) | 数学、字符串、动态规划 | 困难 | | 剑指 Offer 03 | [数组中重复的数字](https://leetcode.cn/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/%E5%89%91%E6%8C%87%20Offer%2003.%20%E6%95%B0%E7%BB%84%E4%B8%AD%E9%87%8D%E5%A4%8D%E7%9A%84%E6%95%B0%E5%AD%97.md) | 数组、哈希表、排序 | 简单 | | 剑指 Offer 04 | [二维数组中的查找](https://leetcode.cn/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/%E5%89%91%E6%8C%87%20Offer%2004.%20%E4%BA%8C%E7%BB%B4%E6%95%B0%E7%BB%84%E4%B8%AD%E7%9A%84%E6%9F%A5%E6%89%BE.md) | 数组、二分查找、分治、矩阵 | 中等 | diff --git a/Contents/00.Introduction/05.Categories-List.md b/Contents/00.Introduction/05.Categories-List.md index fc81455a..3d0ce949 100644 --- a/Contents/00.Introduction/05.Categories-List.md +++ b/Contents/00.Introduction/05.Categories-List.md @@ -965,8 +965,8 @@ | 题号 | 标题 | 题解 | 标签 | 难度 | | :------ | :------ | :------ | :------ | :------ | -| 1155 | [掷骰子等于目标和的方法数](https://leetcode.cn/problems/number-of-dice-rolls-with-target-sum/) | | 动态规划 | 中等 | -| 2585 | [获得分数的方法数](https://leetcode.cn/problems/number-of-ways-to-earn-points/) | | 数组、动态规划 | 困难 | +| 1155 | [掷骰子等于目标和的方法数](https://leetcode.cn/problems/number-of-dice-rolls-with-target-sum/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/1155.%20%E6%8E%B7%E9%AA%B0%E5%AD%90%E7%AD%89%E4%BA%8E%E7%9B%AE%E6%A0%87%E5%92%8C%E7%9A%84%E6%96%B9%E6%B3%95%E6%95%B0.md) | 动态规划 | 中等 | +| 2585 | [获得分数的方法数](https://leetcode.cn/problems/number-of-ways-to-earn-points/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/2585.%20%E8%8E%B7%E5%BE%97%E5%88%86%E6%95%B0%E7%9A%84%E6%96%B9%E6%B3%95%E6%95%B0.md) | 数组、动态规划 | 困难 | #### 多维背包问题 diff --git a/Contents/10.Dynamic-Programming/04.Knapsack-Problem/06.Knapsack-Problem-List.md b/Contents/10.Dynamic-Programming/04.Knapsack-Problem/06.Knapsack-Problem-List.md index 4a0f2850..e2225bdc 100644 --- a/Contents/10.Dynamic-Programming/04.Knapsack-Problem/06.Knapsack-Problem-List.md +++ b/Contents/10.Dynamic-Programming/04.Knapsack-Problem/06.Knapsack-Problem-List.md @@ -26,8 +26,8 @@ | 题号 | 标题 | 题解 | 标签 | 难度 | | :------ | :------ | :------ | :------ | :------ | -| 1155 | [掷骰子等于目标和的方法数](https://leetcode.cn/problems/number-of-dice-rolls-with-target-sum/) | | 动态规划 | 中等 | -| 2585 | [获得分数的方法数](https://leetcode.cn/problems/number-of-ways-to-earn-points/) | | 数组、动态规划 | 困难 | +| 1155 | [掷骰子等于目标和的方法数](https://leetcode.cn/problems/number-of-dice-rolls-with-target-sum/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/1155.%20%E6%8E%B7%E9%AA%B0%E5%AD%90%E7%AD%89%E4%BA%8E%E7%9B%AE%E6%A0%87%E5%92%8C%E7%9A%84%E6%96%B9%E6%B3%95%E6%95%B0.md) | 动态规划 | 中等 | +| 2585 | [获得分数的方法数](https://leetcode.cn/problems/number-of-ways-to-earn-points/) | [Python](https://github.com/itcharge/LeetCode-Py/blob/main/Solutions/2585.%20%E8%8E%B7%E5%BE%97%E5%88%86%E6%95%B0%E7%9A%84%E6%96%B9%E6%B3%95%E6%95%B0.md) | 数组、动态规划 | 困难 | #### 多维背包问题 diff --git a/README.md b/README.md index 8e615c29..6af512aa 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## 项目简介 -- **「算法与数据结构」** 基础知识的讲解教程,「LeetCode」750+ 道题目的详细解析。本项目易于理解,没有大跨度的思维跳跃,项目中使用部分图示、例子来帮助理解。 +- **「算法与数据结构」** 基础知识的讲解教程,「LeetCode」800+ 道题目的详细解析。本项目易于理解,没有大跨度的思维跳跃,项目中使用部分图示、例子来帮助理解。 - 本教程先从基础的数据结构和算法开始讲解,再针对不同分类的数据结构和算法,进行具体题目的讲解分析。让读者可以通过「算法基础理论学习」和「编程实战学习」相结合的方式,彻底的掌握算法知识。 @@ -26,7 +26,7 @@ 我是一名 iOS / macOS 的开发程序员,另外也是北航软院的一名非全硕士(在读)。曾在大学期间学习过算法知识,并参加过 3 年的 ACM 比赛, 但水平有限,未能取得理想成绩。但是这 3 年的 ACM 经历,给我最大的收获是锻炼了自己的逻辑思维和解决实际问题的能力,这种能力为我今后的工作、学习打下了坚实的基础。 -我从 2021 年 03 月 30 日开始每日在 LeetCode 刷题,到 2022 年 06 月 08 日已经刷了 1000+ 道题目,并且完成了 750+ 道题解。努力向着 1000+、1500+、2000+ 道题解前进。 +我从 2021 年 03 月 30 日开始每日在 LeetCode 刷题,到 2022 年 06 月 08 日已经刷了 1000+ 道题目,并且完成了 800+ 道题解。努力向着 1000+、1500+、2000+ 道题解前进。 在公众号 **「程序员充电站」** 里回复 "**算法打卡**",拉你进 LeetCode 算法打卡计划群一起组队打卡。 @@ -259,4 +259,4 @@ - [动态规划优化题目](./Contents/10.Dynamic-Programming/11.DP-Optimization/04.DP-Optimization-List.md) ## 11. 附加内容 -## [12. LeetCode 题解(已完成 799 道)](./Contents/00.Introduction/04.Solutions-List.md) \ No newline at end of file +## [12. LeetCode 题解(已完成 801 道)](./Contents/00.Introduction/04.Solutions-List.md) \ No newline at end of file diff --git "a/Solutions/0063. 344円270円215円345円220円214円350円267円257円345円276円204円 II.md" "b/Solutions/0063. 344円270円215円345円220円214円350円267円257円345円276円204円 II.md" index 48aa369d..9beb7351 100644 --- "a/Solutions/0063. 344円270円215円345円220円214円350円267円257円345円276円204円 II.md" +++ "b/Solutions/0063. 344円270円215円345円220円214円350円267円257円345円276円204円 II.md" @@ -5,17 +5,68 @@ ## 题目大意 -一个机器人位于一个 `m x n` 网格的左上角。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角。但是网格中有障碍物,不能通过。 +**描述**:一个机器人位于一个 $m \times n$ 网格的左上角。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角。但是网格中有障碍物,不能通过。 -现在给定一个二维数组表示网格,1 代表障碍物,0 表示空位。要求计算出:从左上角到右下角会有多少条不同的路径。 +现在给定一个二维数组表示网格,1ドル$ 代表障碍物,0ドル$ 表示空位。 + +**要求**:计算出从左上角到右下角会有多少条不同的路径。 + +**说明**: + +- $m == obstacleGrid.length$。 +- $n == obstacleGrid[i].length$。 +- 1ドル \le m, n \le 100$。 +- $obstacleGrid[i][j]$ 为 0ドル$ 或 1ドル$。 + +**示例**: + +- 示例 1: + +![](https://assets.leetcode.com/uploads/2020/11/04/robot1.jpg) + +```Python +输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]] +输出:2 +解释:3x3 网格的正中间有一个障碍物。 +从左上角到右下角一共有 2 条不同的路径: +1. 向右 -> 向右 -> 向下 -> 向下 +2. 向下 -> 向下 -> 向右 -> 向右 +``` + +- 示例 2: + +![](https://assets.leetcode.com/uploads/2020/11/04/robot2.jpg) + +```Python +输入:obstacleGrid = [[0,1],[0,0]] +输出:1 +``` ## 解题思路 -可以用动态规划求解,设 `dp[i][j]` 是从 `(0, 0)` 到 `(i, j)` 的不同路径数。显然 `dp[i][j] = dp[i-1][j] + dp[i][j-1]`。对于第一行、第一列,因为只能超一个方向走,所以 `dp[i][0] = 1`,`dp[0][j] = 1`。如果在第一行、第一列遇到障碍,则终止赋值,跳出循环。 +### 思路 1:动态规划 + +###### 1. 划分阶段 + +按照路径的结尾位置(行位置、列位置组成的二维坐标)进行阶段划分。 + +###### 2. 定义状态 + +定义状态 $dp[i][j]$ 表示为:从 $(0, 0)$ 到 $(i, j)$ 的不同路径数。 + +###### 3. 状态转移方程 + +因为我们每次只能向右、或者向下移动一步,因此想要走到 $(i, j),ドル只能从 $(i - 1, j)$ 向下走一步走过来;或者从 $(i, j - 1)$ 向右走一步走过来。则状态转移方程为:$dp[i][j] = dp[i - 1][j] + dp[i][j - 1],ドル其中 $obstacleGrid[i][j] == 0$。 -然后从上到下,从左到右依次遍历,递推求解,遇到障碍物就跳过。最终输出 `dp[m - 1][n - 1]` 即可。 +###### 4. 初始条件 -## 代码 +- 对于第一行、第一列,因为只能超一个方向走,所以 $dp[i][0] = 1,ドル$dp[0][j] = 1$。如果在第一行、第一列遇到障碍,则终止赋值,跳出循环。 + +###### 5. 最终结果 + +根据我们之前定义的状态,$dp[i][j]$ 表示为:从 $(0, 0)$ 到 $(i, j)$ 的不同路径数。所以最终结果为 $dp[m - 1][n - 1]$。 + +### 思路 1:代码 ```Python class Solution: @@ -40,6 +91,10 @@ class Solution: continue dp[i][j] = dp[i - 1][j] + dp[i][j - 1] return dp[m - 1][n - 1] - ``` +### 思路 1:复杂度分析 + +- **时间复杂度**:$O(m \times n)$。 +- **空间复杂度**:$O(m \times n)$。 + diff --git "a/Solutions/0474. 344円270円200円345円222円214円351円233円266円.md" "b/Solutions/0474. 344円270円200円345円222円214円351円233円266円.md" index 3dba1af3..78909bc1 100644 --- "a/Solutions/0474. 344円270円200円345円222円214円351円233円266円.md" +++ "b/Solutions/0474. 344円270円200円345円222円214円351円233円266円.md" @@ -5,28 +5,71 @@ ## 题目大意 -给定一个二进制字符串数组 `strs`,以及两个整数 `m` 和 `n`。 +**描述**:给定一个二进制字符串数组 $strs,ドル以及两个整数 $m$ 和 $n$。 -要求:找出并返回 `strs` 的最大子集大小,该子集中最多有 `m` 个 0 和 `n` 个 1。 +**要求**:找出并返回 $strs$ 的最大子集的大小,该子集中最多有 $m$ 个 0ドル$ 和 $n$ 个 1ドル$。 -如果 `x` 的所有元素也是 `y` 的元素,集合 `x` 是集合 `y` 的子集。 +**说明**: + +- 如果 $x$ 的所有元素也是 $y$ 的元素,集合 $x$ 是集合 $y$ 的子集。 +- 1ドル \le strs.length \le 600$。 +- 1ドル \le strs[i].length \le 100$。 +- $strs[i]$ 仅由 `'0'` 和 `'1'` 组成。 +- 1ドル \le m, n \le 100$。 + +**示例**: + +- 示例 1: + +```Python +输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3 +输出:4 +解释:最多有 5 个 0 和 3 个 1 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。 +其他满足题意但较小的子集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不满足题意,因为它含 4 个 1 ,大于 n 的值 3。 +``` + +- 示例 2: + +```Python +输入:strs = ["10", "0", "1"], m = 1, n = 1 +输出:2 +解释:最大的子集是 {"0", "1"} ,所以答案是 2。 +``` ## 解题思路 -可以转换为 0-1 背包问题来做。 +### 思路 1:动态规划 + +这道题可以转换为「二维 0-1 背包问题」来做。 -把 `0` 的个数和 `1 ` 的个数视作一个二维背包的容量。每一个字符串都当做是一件物品,其成本为字符串中 `1` 的数量和 `0` 的数量,每个字符串的价值为 1。 +把 0ドル$ 的个数和 1ドル$ 的个数视作一个二维背包的容量。每一个字符串都当做是一件物品,其成本为字符串中 1ドル$ 的数量和 0ドル$ 的数量,每个字符串的价值为 1ドル$。 -则可以定义状态 `dp[i][j]` 为:最多有 `i` 个 `0` 和 `j` 个 `1` 的字符串 `strs` 的最大子集的大小为 `dp[i][j]`。 +###### 1. 划分阶段 -则递推公式为:`dp[i][j] = max(dp[i][j], dp[i - zero_num][j - one_num] + 1)`。 +按照物品的序号、当前背包的载重上限进行阶段划分。 -意思为:填满由 `i` 个 `0` 和 `j` 个 `1 ` 构成的二维背包的最多物品数为下面两种情况中的最大值: +###### 2. 定义状态 -- 使用之前字符串填满容量为 `i - zero_num`、`j - one_num` 的背包的物品数 + 当前字符串价值 -- 选择之前字符串填满容量为 `i`、`j` 的物品数。 +定义状态 $dp[i][j]$ 表示为:最多有 $i$ 个 0ドル$ 和 $j$ 个 1ドル$ 的字符串 $strs$ 的最大子集的大小。 -## 代码 +###### 3. 状态转移方程 + +填满最多由 $i$ 个 0ドル$ 和 $j$ 个 1ドル$ 构成的二维背包的最多物品数为下面两种情况中的最大值: + +- 使用之前字符串填满容量为 $i - zero\underline{}num$、$j - one\underline{}num$ 的背包的物品数 + 当前字符串价值 +- 选择之前字符串填满容量为 $i$、$j$ 的物品数。 + +则状态转移方程为:$dp[i][j] = max(dp[i][j], dp[i - zero\underline{}num][j - one\underline{}num] + 1)$。 + +###### 4. 初始条件 + +- 无论有多少个 0ドル,ドル多少个 1ドル,ドル只要不选 0ドル,ドル也不选 1ドル,ドル则最大子集的大小为 0ドル$。 + +###### 5. 最终结果 + +根据我们之前定义的状态,$dp[i][j]$ 表示为:最多有 $i$ 个 0ドル$ 和 $j$ 个 1ドル$ 的字符串 $strs$ 的最大子集的大小。所以最终结果为 $dp[m][n]$。 + +### 思路 1:代码 ```Python class Solution: @@ -48,3 +91,8 @@ class Solution: return dp[m][n] ``` +### 思路 1:复杂度分析 + +- **时间复杂度**:$O(l \times m \times n),ドル其中 $l$ 为字符串 $strs$ 的长度。 +- **空间复杂度**:$O(m \times n)$。 + diff --git "a/Solutions/0576. 345円207円272円347円225円214円347円232円204円350円267円257円345円276円204円346円225円260円.md" "b/Solutions/0576. 345円207円272円347円225円214円347円232円204円350円267円257円345円276円204円346円225円260円.md" index 41ed525e..f8ff84d9 100644 --- "a/Solutions/0576. 345円207円272円347円225円214円347円232円204円350円267円257円345円276円204円346円225円260円.md" +++ "b/Solutions/0576. 345円207円272円347円225円214円347円232円204円350円267円257円345円276円204円346円225円260円.md" @@ -5,9 +5,9 @@ ## 题目大意 -**描述**:有一个大小为 `m * n` 的网络和一个球。球的起始位置为 `[startRow, startColumn]`。你可以将球移到在四个方向上相邻的单元格内(可以穿过网格边界到达网格之外)。最多可以移动 `maxMove` 次球。 +**描述**:有一个大小为 $m \times n$ 的网络和一个球。球的起始位置为 $(startRow, startColumn)$。你可以将球移到在四个方向上相邻的单元格内(可以穿过网格边界到达网格之外)。最多可以移动 $maxMove$ 次球。 -现在给定五个整数 `m`、`n`、`maxMove`、`startRow` 以及 `startColumn`。 +现在给定五个整数 $m$、$n$、$maxMove$、$startRow$ 以及 $startColumn$。 **要求**:找出并返回可以将球移出边界的路径数量。因为答案可能非常大,返回对 10ドル^9 + 7$ 取余后的结果。 @@ -31,9 +31,58 @@ ## 解题思路 -### 思路 1:动态规划 +### 思路 1:记忆化搜索 -我们需要统计从 `[startRow, startColumn]` 位置出发,最多移动 `maxMove` 次能够穿过边界的所有路径数量。则我们可以根据位置和移动步数来划分阶段和定义状态。 +1. 问题的状态定义为:从位置 $(i, j)$ 出发,最多使用 $moveCount$ 步,可以将球移出边界的路径数量。 +2. 定义一个 $m \times n \times (maxMove + 1)$ 的三维数组 $memo$ 用于记录已经计算过的路径数量。 +3. 定义递归函数 $dfs(i, j, moveCount)$ 用于计算路径数量。 + 1. 如果 $(i, j)$ 已经出界,则说明找到了一条路径,返回方案数为 1ドル$。 + 2. 如果没有移动次数了,则返回方案数为 0ドル$。 + 3. 定义方案数 $ans,ドル遍历四个方向,递归计算四个方向的方案数,累积到 $ans$ 中,并进行取余。 + 4. 返回方案数 $ans$。 +4. 调用递归函数 $dfs(startRow, startColumn, maxMove),ドル并将其返回值作为答案进行返回。 + +### 思路 1:代码 + +```Python +class Solution: + def findPaths(self, m: int, n: int, maxMove: int, startRow: int, startColumn: int) -> int: + directions = {(1, 0), (-1, 0), (0, 1), (0, -1)} + mod = 10 ** 9 + 7 + + memo = [[[-1 for _ in range(maxMove + 1)] for _ in range(n)] for _ in range(m)] + + def dfs(i, j, moveCount): + if i < 0 or i>= m or j < 0 or j>= n: + return 1 + + if moveCount == 0: + return 0 + + if memo[i][j][moveCount] != -1: + return memo[i][j][moveCount] + + ans = 0 + for direction in directions: + new_i = i + direction[0] + new_j = j + direction[1] + ans += dfs(new_i, new_j, moveCount - 1) + ans %= mod + + memo[i][j][moveCount] = ans + return ans + + return dfs(startRow, startColumn, maxMove) +``` + +### 思路 1:复杂度分析 + +- **时间复杂度**:$O(m \times n \times maxMove)$。 +- **空间复杂度**:$O(m \times n \times maxMove)$。 + +### 思路 2:动态规划 + +我们需要统计从 $(startRow, startColumn)$ 位置出发,最多移动 $maxMove$ 次能够穿过边界的所有路径数量。则我们可以根据位置和移动步数来划分阶段和定义状态。 ###### 1. 划分阶段 @@ -41,23 +90,23 @@ ###### 2. 定义状态 -定义状态 `dp[i][j][k]` 表示为:从位置 `[i, j]` 最多移动 `k` 次最终穿过边界的所有路径数量。 +定义状态 $dp[i][j][k]$ 表示为:从位置 $(i, j)$ 最多移动 $k$ 次最终穿过边界的所有路径数量。 ###### 3. 状态转移方程 -因为球可以在上下左右四个方向上进行移动,所以对于位置 `[i, j]`,最多移动 `k` 次最终穿过边界的所有路径数量取决于周围四个方向上最多经过 `k - 1` 次穿过对应位置上的所有路径数量和。 +因为球可以在上下左右四个方向上进行移动,所以对于位置 $(i, j),ドル最多移动 $k$ 次最终穿过边界的所有路径数量取决于周围四个方向上最多经过 $k - 1$ 次穿过对应位置上的所有路径数量和。 -即 `dp[i][j][k] = dp[i - 1][j][k - 1] + dp[i + 1][j][k - 1] + dp[i][j - 1][k - 1] + dp[i][j + 1][k - 1]`。 +即:$dp[i][j][k] = dp[i - 1][j][k - 1] + dp[i + 1][j][k - 1] + dp[i][j - 1][k - 1] + dp[i][j + 1][k - 1]$。 ###### 4. 初始条件 -如果位置 `[i, j]` 已经处于边缘,只差一步就穿过边界。则此时位置 `[i, j]` 最多移动 `k` 次最终穿过边界的所有路径数量取决于有相邻多少个方向是边界。也可以通过对上面 `[i - 1, j]`、`[i + 1][j]`、`[i][j - 1]`、`[i, j + 1]` 是否已经穿过边界进行判断(每一个方向穿过一次,就累积一次),来计算路径数目。然后将其作为初始条件。 +如果位置 $[i, j]$ 已经处于边缘,只差一步就穿过边界。则此时位置 $(i, j)$ 最多移动 $k$ 次最终穿过边界的所有路径数量取决于有相邻多少个方向是边界。也可以通过对上面 $(i - 1, j)$、$(i + 1, j)$、$(i, j - 1)$、$(i, j + 1)$ 是否已经穿过边界进行判断(每一个方向穿过一次,就累积一次),来计算路径数目。然后将其作为初始条件。 ###### 5. 最终结果 -根据我们之前定义的状态,`dp[i][j][k]` 表示为:从位置 `[i, j]` 最多移动 `k` 次最终穿过边界的所有路径数量。则最终答案为 `dp[startRow][startColumn][maxMove]`。 +根据我们之前定义的状态,$dp[i][j][k]$ 表示为:从位置 $(i, j)$ 最多移动 $k$ 次最终穿过边界的所有路径数量。则最终答案为 $dp[startRow][startColumn][maxMove]$。 -### 思路 1:动态规划代码 +### 思路 2:动态规划代码 ```Python class Solution: @@ -81,7 +130,7 @@ class Solution: return dp[startRow][startColumn][maxMove] ``` -### 思路 1:复杂度分析 +### 思路 2:复杂度分析 -- **时间复杂度**:$O(m * n * maxMove)$。三重循环遍历的时间复杂度为 $O(m * n * maxMove)$。 -- **空间复杂度**:$O(m * n * maxMove)$。使用了三维数组保存状态,所以总体空间复杂度为 $O(m * n * maxMove)$。 +- **时间复杂度**:$O(m \times n \times maxMove)$。三重循环遍历的时间复杂度为 $O(m \times n \times maxMove)$。 +- **空间复杂度**:$O(m \times n \times maxMove)$。使用了三维数组保存状态,所以总体空间复杂度为 $O(m \times n \times maxMove)$。 diff --git "a/Solutions/1155. 346円216円267円351円252円260円345円255円220円347円255円211円344円272円216円347円233円256円346円240円207円345円222円214円347円232円204円346円226円271円346円263円225円346円225円260円.md" "b/Solutions/1155. 346円216円267円351円252円260円345円255円220円347円255円211円344円272円216円347円233円256円346円240円207円345円222円214円347円232円204円346円226円271円346円263円225円346円225円260円.md" new file mode 100644 index 00000000..db3d98a2 --- /dev/null +++ "b/Solutions/1155. 346円216円267円351円252円260円345円255円220円347円255円211円344円272円216円347円233円256円346円240円207円345円222円214円347円232円204円346円226円271円346円263円225円346円225円260円.md" @@ -0,0 +1,89 @@ +# [1155. 掷骰子等于目标和的方法数](https://leetcode.cn/problems/number-of-dice-rolls-with-target-sum/) + +- 标签:动态规划 +- 难度:中等 + +## 题目大意 + +**描述**:有 $n$ 个一样的骰子,每个骰子上都有 $k$ 个面,分别标号为 1ドル \sim k$。现在给定三个整数 $n$、$k$ 和 $target,ドル滚动 $n$ 个骰子。 + +**要求**:计算出使所有骰子正面朝上的数字和等于 $target$ 的方案数量。 + +**说明**: + +- 1ドル \le n, k \le 30$。 +- 1ドル \le target \le 1000$。 + +**示例**: + +- 示例 1: + +```Python +输入:n = 1, k = 6, target = 3 +输出:1 +解释:你扔一个有 6 个面的骰子。 +得到 3 的和只有一种方法。 +``` + +- 示例 2: + +```Python +输入:n = 2, k = 6, target = 7 +输出:6 +解释:你扔两个骰子,每个骰子有 6 个面。 +得到 7 的和有 6 种方法 1+6 2+5 3+4 4+3 5+2 6+1。 +``` + +## 解题思路 + +### 思路 1:动态规划 + +我们可以将这道题转换为「分组背包问题」中求方案总数的问题。将每个骰子看做是一组物品,骰子每一个面上的数值当做是每组物品中的一个物品。这样问题就转换为:用 $n$ 个骰子($n$ 组物品)进行投掷,投掷出总和(总价值)为 $target$ 的方案数。 + +###### 1. 划分阶段 + +按照总价值 $target$ 进行阶段划分。 + +###### 2. 定义状态 + +定义状态 $dp[w]$ 表示为:用 $n$ 个骰子($n$ 组物品)进行投掷,投掷出总和(总价值)为 $w$ 的方案数。 + +###### 3. 状态转移方程 + +用 $n$ 个骰子($n$ 组物品)进行投掷,投掷出总和(总价值)为 $w$ 的方案数,等于用 $n$ 个骰子($n$ 组物品)进行投掷,投掷出总和(总价值)为 $w - d$ 的方案数累积值,其中 $d$ 为当前骰子掷出的价值,即:$dp[w] = dp[w] + dp[w - d]$。 + +###### 4. 初始条件 + +- 用 $n$ 个骰子($n$ 组物品)进行投掷,投掷出总和(总价值)为 0ドル$ 的方案数为 1ドル$。 + +###### 5. 最终结果 + +根据我们之前定义的状态, $dp[w]$ 表示为:用 $n$ 个骰子($n$ 组物品)进行投掷,投掷出总和(总价值)为 $w$ 的方案数。则最终结果为 $dp[target]$。 + +### 思路 1:代码 + +```Python +class Solution: + def numRollsToTarget(self, n: int, k: int, target: int) -> int: + dp = [0 for _ in range(target + 1)] + dp[0] = 1 + MOD = 10 ** 9 + 7 + + # 枚举前 i 组物品 + for i in range(1, n + 1): + # 逆序枚举背包装载重量 + for w in range(target, -1, -1): + dp[w] = 0 + # 枚举第 i - 1 组物品能取个数 + for d in range(1, k + 1): + if w>= d: + dp[w] = (dp[w] + dp[w - d]) % MOD + + return dp[target] % MOD +``` + +### 思路 1:复杂度分析 + +- **时间复杂度**:$O(n \times m \times target)$。 +- **空间复杂度**:$O(target)$。 + diff --git "a/Solutions/2585. 350円216円267円345円276円227円345円210円206円346円225円260円347円232円204円346円226円271円346円263円225円346円225円260円.md" "b/Solutions/2585. 350円216円267円345円276円227円345円210円206円346円225円260円347円232円204円346円226円271円346円263円225円346円225円260円.md" new file mode 100644 index 00000000..da8af8f5 --- /dev/null +++ "b/Solutions/2585. 350円216円267円345円276円227円345円210円206円346円225円260円347円232円204円346円226円271円346円263円225円346円225円260円.md" @@ -0,0 +1,105 @@ +# [2585. 获得分数的方法数](https://leetcode.cn/problems/number-of-ways-to-earn-points/) + +- 标签:数组、动态规划 +- 难度:困难 + +## 题目大意 + +**描述**:考试中有 $n$ 种类型的题目。给定一个整数 $target$ 和一个下标从 0ドル$ 开始的二维整数数组 $types,ドル其中 $types[i] = [count_i, marks_i]$ 表示第 $i$ 种类型的题目有 $count_i$ 道,每道题目对应 $marks_i$ 分。 + +**要求**:返回你在考试中恰好得到 $target$ 分的方法数。由于答案可能很大,结果需要对 10ドル^9 + 7$ 取余。 + +**说明**: + +- 同类型题目无法区分。比如说,如果有 3ドル$ 道同类型题目,那么解答第 1ドル$ 和第 2ドル$ 道题目与解答第 1ドル$ 和第 3ドル$ 道题目或者第 2ドル$ 和第 3ドル$ 道题目是相同的。 +- 1ドル \le target \le 1000$。 +- $n == types.length$。 +- 1ドル \le n \le 50$。 +- $types[i].length == 2$。 +- 1ドル \le counti, marksi \le 50$。 + +**示例**: + +- 示例 1: + +```Python +输入:target = 6, types = [[6,1],[3,2],[2,3]] +输出:7 +解释:要获得 6 分,你可以选择以下七种方法之一: +- 解决 6 道第 0 种类型的题目:1 +たす 1 +たす 1 +たす 1 +たす 1 +たす 1 = 6 +- 解决 4 道第 0 种类型的题目和 1 道第 1 种类型的题目:1 +たす 1 +たす 1 +たす 1 +たす 2 = 6 +- 解决 2 道第 0 种类型的题目和 2 道第 1 种类型的题目:1 +たす 1 +たす 2 +たす 2 = 6 +- 解决 3 道第 0 种类型的题目和 1 道第 2 种类型的题目:1 +たす 1 +たす 1 +たす 3 = 6 +- 解决 1 道第 0 种类型的题目、1 道第 1 种类型的题目和 1 道第 2 种类型的题目:1 + 2 + 3 = 6 +- 解决 3 道第 1 种类型的题目:2 + 2 + 2 = 6 +- 解决 2 道第 2 种类型的题目:3 + 3 = 6 +``` + +- 示例 2: + +```Python +输入:target = 5, types = [[50,1],[50,2],[50,5]] +输出:4 +解释:要获得 5 分,你可以选择以下四种方法之一: +- 解决 5 道第 0 种类型的题目:1 +たす 1 +たす 1 +たす 1 +たす 1 = 5 +- 解决 3 道第 0 种类型的题目和 1 道第 1 种类型的题目:1 +たす 1 +たす 1 +たす 2 = 5 +- 解决 1 道第 0 种类型的题目和 2 道第 1 种类型的题目:1 + 2 + 2 = 5 +- 解决 1 道第 2 种类型的题目:5 +``` + +## 解题思路 + +### 思路 1:动态规划 + +###### 1. 划分阶段 + +按照进行阶段划分。 + +###### 2. 定义状态 + +定义状态 $dp[i][w]$ 表示为:前 $i$ 种题目恰好组成 $w$ 分的方案数。 + +###### 3. 状态转移方程 + +前 $i$ 种题目恰好组成 $w$ 分的方案数,等于前 $i - 1$ 种问题恰好组成 $w - k \times marks_i$ 分的方案数总和,即状态转移方程为:$dp[i][w] = \sum_{k = 0} dp[i - 1][w - k \times marks_i]$。 + +###### 4. 初始条件 + +- 前 0ドル$ 种题目恰好组成 0ドル$ 分的方案数为 1ドル$。 + +###### 5. 最终结果 + +根据我们之前定义的状态, $dp[i][w]$ 表示为:前 $i$ 种题目恰好组成 $w$ 分的方案数。 所以最终结果为 $dp[size][target]$。 + +### 思路 1:代码 + +```Python +class Solution: + def waysToReachTarget(self, target: int, types: List[List[int]]) -> int: + size = len(types) + group_count = [types[i][0] for i in range(len(types))] + weight = [[(types[i][1] * k) for k in range(types[i][0] + 1)] for i in range(len(types))] + mod = 1000000007 + + dp = [[0 for _ in range(target + 1)] for _ in range(size + 1)] + dp[0][0] = 1 + + # 枚举前 i 组物品 + for i in range(1, size + 1): + # 枚举背包装载重量 + for w in range(target + 1): + # 枚举第 i 组物品能取个数 + dp[i][w] = dp[i - 1][w] + for k in range(1, group_count[i - 1] + 1): + if w>= weight[i - 1][k]: + dp[i][w] += dp[i - 1][w - weight[i - 1][k]] + dp[i][w] %= mod + + return dp[size][target] +``` + +### 思路 1:复杂度分析 + +- **时间复杂度**:$O(n \times target \times m),ドル其中 $n$ 为题目种类数,$target$ 为目标分数,$m$ 为每种题目的最大分数。 +- **空间复杂度**:$O(n \times target)$。 +

AltStyle によって変換されたページ (->オリジナル) /