题目:剑指 Offer 42. 连续子数组的最大和
难度:简单
算法:动态规划
# 2022.04.20
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
# base case
dp = [0] * (len(nums) )
dp[0] = nums[0]
ans = nums[0]
for i in range(1, len(nums) ):
dp[i] = max(dp[i - 1] + nums[i], nums[i])
ans = max(ans, dp[i])
return ans
题目:188. 买卖股票的最佳时机 IV
难度:困难
算法:动态规划
class Solution:
def maxProfit(self, k: int, prices: List[int]) -> int:
# dp[i][k][0]
# dp[i][k][1]
# i base case:
# dp[-1][k][0] = 0
# dp[-1][k][1] = -inf
# k base case:
# dp[i][0][0] = 0
# dp[i][0][1] = -inf
if len(prices) == 0:
return 0
dp = [[[0] * 2] * k] * (len(prices))
print(dp)
for i in range(len(prices)):
dp[i][0][0] = 0
dp[i][0][1] = -inf
for kk in range(1, k):
if i - 1 == 0:
dp[i-1][kk][0] = 0
dp[i-1][kk][1] = -inf
continue
# 状态转移方程
# 现在没持有的话,要么是上期无且rest,要么上期有且本期卖掉
dp[i][kk][0] = max(dp[i-1][kk][0], dp[i-1][kk][1] + prices[i])
# 现在持有的话,要么是上期有且rest,要么是上期无且本期买
dp[i][kk][1] = max(dp[i-1][kk][1], dp[i-1][kk-1][0] - prices[i])
return dp[len(prices)-1][kk][0]
题目:322. 零钱兑换
难度:中等
算法:动态规划(取min值–最大最小问题)
# 2022.08.15
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
dp = [inf] * (amount + 1)
dp[0] = 0
# dp数组的范围都要+1
# 两层循环也可以写成
# for coin in coins:
# for i in range(coin, amount + 1):
# 这样降低复杂度且节省了i-coin的比较
for i in range(1, amount + 1):
# 对每个coin都搜索一次,因为不限数量
for coin in coins:
# 判断一下,否则下面的dp[-coin]会越界
if i - coin >= 0:
# 转移方程:dp[i]为需要达到i数额用到的金币数,dp[i-coin]为需要达到i减去当前coin数额用到的金币数
dp[i] = min(dp[i], dp[i - coin] + 1)
return dp[amount] if dp[amount] != inf else -1
题目:518. 零钱兑换 II
难度:中等
算法:动态规划(取汇总值–组合问题)
# 2022.08.16
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
# dp[i]为金额总和等于i的金币组合数
dp = [0] * (amount + 1)
# base case
# dp[0] = 1,因为等于0的方案只有1个,就是什么coin都不选
dp[0] = 1
for coin in coins:
for i in range(coin, amount + 1):
# 把到每个dp从不同途径的coin组合起来的组合数累加起来
dp[i] += dp[i - coin]
return dp[amount]
# coin = 1
# dp[0] = 1
# dp[1] += dp[0] = 1
# dp[2] += dp[1] = 1
# dp[3] += dp[2] = 1
# dp[4] += dp[3] = 1
# dp[5] += dp[4] = 1
# coin = 2
# dp[2] += dp[0] = 2
# dp[3] += dp[1] = 2
# dp[4] += dp[2] = 3 # coin是2的时候,确定从amount=2可以到amount=4,那么就要看达到amount=2有几种方案
# dp[5] += dp[3] = 3
# coin = 5
# dp[5] += dp[0] = 4
题目:139. 单词拆分
难度:中等
算法:动态规划(True、False问题)
tips:看s在不在wordDict中,而不是wordDict在不在s中
# 2022.08.16
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
n = len(s)
# 到j的时候,s[i:j]在wordDict中,那么追溯dp[i]也在wordDict里,则dp[j]也True
dp = [False] * (n + 1)
dp[0] = True
# 注意i的范围,不能跟其他动态规划一样从1开始,因为后面有个s[i:j]的判断,要把s[0]也包含进去
for i in range(0, n + 1):
# j的范围从i开始或从i+1开始都可以,因为s[i:i]为空,会跳过
for j in range(i + 1, n + 1):
if dp[i] and s[i: j] in wordDict:
dp[j] = True
return dp[n]
题目:121. 买卖股票的最佳时机
难度:简单
算法:动态规划
# 2022.09.08
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
# dp[i][0]为第i天的手上没股票的最大利润,dp[i][1]为第i天的手上持有股票的最大利润
dp = [[0] * 2] * n
# 不能用range(1, n+1),否则prices[i]的index会溢出
for i in range(n):
if i-1 == -1:
dp[i][0] = 0
dp[i][1] = -prices[i]
# 当前没股票,那可能是上期有本期卖了,或上期没持有
dp[i][0] = max(dp[i-1][1] + prices[i], dp[i-1][0])
# 当前持有股票,那可能是上期没持有+本期买了,或上期有
# 下面这个是错的,因为只有一次买卖机会,所以只要买了一次,默认当前的利润为- prices[i],否则会认为是叠加了之前的利润
# dp[i][1] = max(dp[i-1][0] - prices[i], dp[i-1][1])
dp[i][1] = max(- prices[i], dp[i-1][1])
# 不需要return max(dp[n-1][0], dp[n-1][1]),因为如果到最后手里还持有股票没卖出的话,利润肯定没有卖出的高
return dp[n-1][0]片
题目:122. 买卖股票的最佳时机 II
难度:中等
算法:动态规划
# 2022.09.12
class Solution:
def maxProfit(self, prices: List[int]) -> int:
if prices == sorted(prices, reverse = True):
return 0
n = len(prices)
dp = [[0] * 2] * n
# dp[i][1]当前有股票,上期无、本期购买,或上期有、本期无操作
# dp[i][0]当前无股票,上期无、本期无操作,上期有,本期出售
# dp[i][0/1]为第i期最大利润
dp[-1][0] = 0
dp[-1][1] = -prices[0]
for i in range(n):
dp[i][1] = max(dp[i-1][0] - prices[i], dp[i-1][1])
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
return dp[n-1][0]