本文主要是介绍力扣练习4.11,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
452. 用最少数量的箭引爆气球
不考虑y轴,可以将其转换为重叠区间的问题。将同属于一个重叠区间的小区间合并为一个区间,加上不重叠的区间,即是所求数量。
更加简化:如果是非重叠区间才加1,因为两个大的重叠区间间肯定是非重叠的。
class Solution:def findMinArrowShots(self, points: List[List[int]]) -> int:if not points:return 0# 根据右区间排序points.sort(key=lambda x: x[1])# 结果,所需数量res = 1 # 初始化为1,这样当仅有一个区间时也能正确返回# 区间末尾end = points[0][1]# 遍历剩余区间for i in range(1, len(points)):# 如果当前区间左界超过了区间末尾,说明不重叠if points[i][0] > end:res += 1 # 结果加一end = points[i][1] # 更新区间末尾return res
1710. 卡车上的最大单元数
贪婪算法,每次优先使用最能装的箱子,也要判断箱子数和该类箱子数的大小。
class Solution:def maximumUnits(self, boxTypes: List[List[int]], truckSize: int) -> int:# 结果数res = 0# 根据单元数倒排boxTypes.sort(key=lambda x: x[1], reverse=True)# 索引i = 0# 限定循环条件while truckSize > 0 and i < len(boxTypes):# 如果说箱子数比该类箱子多,就使用全部该箱子if boxTypes[i][0] <= truckSize:res += boxTypes[i][0] * boxTypes[i][1]truckSize -= boxTypes[i][0] # 更新箱子数# 否则,只能使用全部箱子数else:res += truckSize * boxTypes[i][1]truckSize = 0 # 更新箱子数i += 1 # 查找下一类箱子return res
494. 目标和
问题转化为0-1背包问题
题目要求我们计算通过在nums
数组中的每个数字前添加+
或-
,使得它们的总和等于给定的目标值target
的方法数。实际上,这个问题可以转化为一个经典的0-1背包问题。
首先,假设我们把所有选用+
的数字放入一个背包A中,所有选用-
的数字放入另一个背包B中。那么,背包A的总和减去背包B的总和应该等于target
。同时,背包A和背包B中数字的总和加起来就是nums
数组中所有数字的总和,记为sum
。基于这两个条件,我们可以得到以下两个等式:
sumA - sumB = target
sumA + sumB = sum
通过解这两个等式,我们可以得到 sumA = (target + sum) / 2
。这意味着问题转化为了寻找nums
数组中有多少种组合,使得这些组合的和等于(target + sum) / 2
。
解决0-1背包问题
这个转化后的问题是一个标准的0-1背包问题:给定一个容量为(target + sum) / 2
的背包,nums
数组中的每个数字都可以选择放入或不放入背包,求有多少种方法可以恰好填满背包。
我们可以使用动态规划来解决这个问题。定义一个一维数组dp
,其中dp[j]
表示用nums
数组中的数字填满容量为j
的背包的方法数。初始化dp[0] = 1
,表示容量为0的背包有一种填法(即不放入任何数字)。
然后,对于nums
数组中的每个数字num
,我们更新dp
数组:对于每个容量j
(从(target + sum) / 2
到num
),如果j >= num
,则dp[j] += dp[j - num]
。这表示如果当前数字可以放入容量为j
的背包中,那么填满容量为j
的背包的方法数就等于之前填满容量为j
的方法数(不放入当前数字)加上填满容量为j - num
的方法数(放入当前数字)。
代码实现
class Solution:def findTargetSumWays(self, nums: List[int], target: int) -> int:total_sum = sum(nums)if abs(target) > total_sum or (target + total_sum) % 2 != 0:return 0package_a = (target + total_sum) // 2dp = [0] * (package_a + 1)dp[0] = 1for num in nums:for j in range(package_a, num - 1, -1):dp[j] += dp[j - num]return dp[package_a]
示例
输入:
nums = [1, 1, 1, 1, 1], target = 3
输出:5
解释: 有五种方法使得和为3:
+1+1+1-1-1
+1+1-1+1-1
+1-1+1+1-1
-1+1+1+1+1
+1-1-1+1+1
通过将问题转化为0-1背包问题,并应用动态规划方法,我们可以高效地求解出结果。
1137. 第 N 个泰波那契数
使用动态规划,下面是直接规划
class Solution:def tribonacci(self, n: int) -> int:# 直接在状态数组dp中初始化前三个Tribonacci数dp = [0, 1, 1] + [0] * (n - 2)# 从第4个数开始计算(索引3),因为前三个数已经初始化for i in range(3, n + 1):dp[i] = dp[i - 3] + dp[i - 2] + dp[i - 1]return dp[n]
规避了要判断n是否小于3的操作,因为直接对dp的前三个进行赋值。
dp数组的前三个值被初始化为[0, 1, 1],这对应于n = 0、n = 1和n = 2的情况。如果n小于3,dp[n]将直接返回正确的结果,无需额外的条件判断。对于n >= 3的情况,for循环将按照Tribonacci数的定义计算每个数值,并存储在dp数组中。最后,直接返回dp[n]作为结果。
记忆化搜索方法:
class Solution:def tribonacci(self, n: int) -> int:# 保存记忆memo = [0 for _ in range(n+1)]return self.do(n, memo)def do(self, n, memo):if n == 0:return 0elif n < 3:return 1# 检查是否已经计算过if memo[n] != 0:return memo[n]# 如果没有被计算过memo[n] = self.do(n-3, memo) + self.do(n-2, memo) + self.do(n-1, memo)return memo[n]
576. 出界的路径数
题目概述
题目“576.
出界的路径数”要求计算一个点从网格中的特定位置开始,最多移动N
步后,有多少种路径可以使它移出网格的边界。每一步,这个点可以向上、下、左、右四个方向之一移动。解题思路
解决这个问题的关键是理解我们如何使用动态规划(DP)来跟踪每一步移动后各个位置的路径数量,并最终计算出所有导致出界的路径。
动态规划
- 定义状态: 我们定义一个二维数组
dp[x][y]
来表示当前步骤中从起始位置到达(x, y)
的路径数。- 状态转移: 对于每一步,我们根据前一步的
dp
数组计算当前步的dp
数组。如果从(x, y)
出发的下一步移动会导致出界,则将这部分路径数累加到最终的计数中。- 初始化: 在第0步,我们将起始位置
(i, j)
的路径数设为1,因为在没有移动之前,存在一条路径停留在起点。为了避免重复计算和节省空间,我们可以使用滚动数组技巧,即只维护两个二维数组交替表示当前步和前一步的状态。
边界处理
由于每一步移动都有可能导致点移出边界,我们需要在每一步检查四个方向的移动是否会导致出界。如果是,我们就把这次移动对应的路径数加到最终的计数中。
取模操作
由于路径数可能非常大,题目要求返回结果对
10^9 + 7
取模的值。这意味着每次累加路径数后,我们都应该对结果进行取模,以保证结果在合法的整数范围内。
代码实现
MOD = 10**9 + 7 # 定义模数,用于取模操作,防止整数溢出class Solution:def findPaths(self, m: int, n: int, N: int, i: int, j: int) -> int:if N == 0: return 0 # 如果没有移动次数,直接返回0dp = [[0] * n for _ in range(m)] # 初始化dp数组,存储每个位置的路径数dp[i][j] = 1 # 将起始位置的路径数设为1count = 0 # 初始化出界路径的计数器for step in range(1, N+1): # 遍历每一步temp = [[0] * n for _ in range(m)] # 初始化临时数组,用于计算下一步的路径数for x in range(m): # 遍历每个位置for y in range(n):for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]: # 遍历四个方向nx, ny = x + dx, y + dy # 计算新位置if 0 <= nx < m and 0 <= ny < n: # 如果新位置在网格内temp[x][y] += dp[nx][ny] # 累加路径数到临时数组else: # 如果新位置出界count += dp[x][y] # 累加当前位置的路径数到出界计数器temp[x][y] %= MOD # 对每一步的结果取模,防止溢出dp = temp # 将临时数组的结果赋值给dp,为下一步做准备return count % MOD # 返回最终的出界路径数,并对结果取模
示例
假设m = 2, n = 2, N = 2, i = 0, j = 0
,则有6种路径可以在2步内从(0, 0)
移出网格边界。
通过逐步应用动态规划并在每一步累加出界的路径数,我们可以找到从给定起始位置移动N步后出界的所有路径数。这种方法通过避免重复计算并优化空间使用,有效解决了问题。
这篇关于力扣练习4.11的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!