198. 打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 400
房间0 房间1 房间2
房间1_max = max(房间1,房间0)
房间2_max = max(房间0 + 房间2,房间1_max)
动态规划 无后效性,只与上一个状态有关
class Solution:
def rob(self, nums: List[int]) -> int:
n = len(nums)
f = [0] *(n+2)
for i in range(2,n+2):
f[i] = max(f[i-2] + nums[i-2],f[i-1])
return f[n+1]
740. 删除并获得点数
给你一个整数数组 nums ,你可以对它进行一些操作。
每次操作中,选择任意一个 nums[i] ,删除它并获得 nums[i] 的点数。之后,你必须删除 所有 等于 nums[i] - 1 和 nums[i] + 1 的元素。
开始你拥有 0 个点数。返回你能通过这些操作获得的最大点数。
示例 1:
输入:nums = [3,4,2]
输出:6
解释:
删除 4 获得 4 个点数,因此 3 也被删除。
之后,删除 2 获得 2 个点数。总共获得 6 个点数。
示例 2:
输入:nums = [2,2,3,3,3,4]
输出:9
解释:
删除 3 获得 3 个点数,接着要删除两个 2 和 4 。
之后,再次删除 3 获得 3 个点数,再次删除 3 获得 3 个点数。
总共获得 9 个点数。
提示:
1 <= nums.length <= 2 * 104
1 <= nums[i] <= 104
该题2个关键点:
1、初始化dp[0] 和 dp[1]的值
2、计算nums中相同的值对应的总和 并建立映射
class Solution:
def deleteAndEarn(self, nums: List[int]) -> int:
nums_map = collections.Counter(nums)
nums = list(set(nums))
nums.sort()
n = len(nums)
nums = [0] + nums
dp = [0,nums[1]*nums_map[nums[1]]] + [0]*(n-1)
for i in range(2,n+1):
if nums[i] == nums[i-1] + 1:
dp[i] = max(dp[i-1],dp[i-2]+nums[i]*nums_map[nums[i]])
else:
dp[i] = dp[i-1] + nums[i]*nums_map[nums[i]]
return dp[n]
2320. 统计放置房子的方式数
一条街道上共有 n * 2 个 地块 ,街道的两侧各有 n 个地块。每一边的地块都按从 1 到 n 编号。每个地块上都可以放置一所房子。
现要求街道同一侧不能存在两所房子相邻的情况,请你计算并返回放置房屋的方式数目。由于答案可能很大,需要对 109 + 7 取余后再返回。
注意,如果一所房子放置在这条街某一侧上的第 i 个地块,不影响在另一侧的第 i 个地块放置房子。
示例 1:
输入:n = 1
输出:4
解释:
可能的放置方式:
1. 所有地块都不放置房子。
2. 一所房子放在街道的某一侧。
3. 一所房子放在街道的另一侧。
4. 放置两所房子,街道两侧各放置一所。
示例 2:
输入:n = 2
输出:9
解释:如上图所示,共有 9 种可能的放置方式。
提示:
1 <= n <= 10**4
考虑第i个地块:
如果第i个地块放置房子时,那么 那么第i-1个地块肯定不能放,放置房子的数量就等于 f[i-2]
如果第i个地块不放置房子,那么第i-1个地块放不房子都无所谓,所以 f[i] = f[i-1]
综上 f[i] = f[i-1] + f[i-2]
class Solution:
def countHousePlacements(self, n: int) -> int:
dp = [1,2] + [0] *(n-1)
for i in range(2,n+1):
dp[i] = dp[i-1] + dp[i-2]
ans = dp[n]*dp[n]
return ans % (10**9+7)
213. 打家劫舍 II
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
示例 1:
输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:
输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 3:
输入:nums = [1,2,3]
输出:3
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 1000
因为第一间房屋和最后一间房屋不能同时被偷,所以分两种情况讨论,保留第一间房屋和保留最后一间房屋
class Solution:
def rob(self, nums: List[int]) -> int:
def max_num(new_nums):
n = len(new_nums)
f = [0] * (n+2)
for i in range(2,n+2):
f[i] = max(f[i-2] + new_nums[i-2],f[i-1])
return f[n+1]
if len(nums) == 1:
return nums[0]
else:
return max(max_num(nums[1:]),max_num(nums[:-1]))
3186. 施咒的最大总伤害
一个魔法师有许多不同的咒语。
给你一个数组 power ,其中每个元素表示一个咒语的伤害值,可能会有多个咒语有相同的伤害值。
已知魔法师使用伤害值为 power[i] 的咒语时,他们就 不能 使用伤害为 power[i] - 2 ,power[i] - 1 ,power[i] + 1 或者 power[i] + 2 的咒语。
每个咒语最多只能被使用 一次 。
请你返回这个魔法师可以达到的伤害值之和的 最大值 。
示例 1:
输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:
输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 3:
输入:nums = [1,2,3]
输出:3
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 1000
注意同等增加 f 和 数组的长度,增加3个,方便计算
这里首先排序,然后分情况讨论
class Solution:
def maximumTotalDamage(self, power: List[int]) -> int:
nums = list(set(power))
nums.sort()
nums_map = collections.Counter(power)
n = len(nums)
f = [0] * (n+3)
nums = [0]*3 + nums
for i in range(3,n+3):
if nums[i] - nums[i-1] == 1:
if nums[i] - nums[i-2] == 2:
f[i] = max(f[i-3] +nums[i] * nums_map[nums[i]],f[i-1])
else:
f[i] = max(f[i-2] +nums[i] * nums_map[nums[i]],f[i-1])
elif nums[i] - nums[i-1] == 2:
f[i] = max(f[i-2] +nums[i] * nums_map[nums[i]],f[i-1])
else:
f[i] = f[i-1] + nums[i] * nums_map[nums[i]]
return f[n+2]