模板
https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/discuss/108870/Most-consistent-ways-of-dealing-with-the-series-of-stock-problems
python 版本的解法
LeetCode:188. 买卖股票的最佳时机 IV(python)_wk的博客-CSDN博客
122. 买卖股票的最佳时机 II
贪心
123 买卖股票的最佳时机3
与121买卖股票的最佳时机相同的解法,就是加一个从右边到左边的遍历。
从右边到左边的遍历,记录区间[i,j]之间的最大收益,用最大值-prices[i]。
class Solution(object):
def maxProfit(self, prices):
"""
:type prices: List[int]
:rtype: int
"""
#两次,分为左边和右边
#左边就是1次收益的最大值那种情况
min_price_left=prices[0]
res_left=[0]
for i in range(1,len(prices)):
diff=prices[i]-min_price_left
if prices[i]<min_price_left:
min_price_left=prices[i]
if diff>res_left[-1]:
res_left.append(diff)
else:
res_left.append(res_left[-1])
#从右边到左边
max_prices_right=prices[-1]
res_right=[0]
for i in range(len(prices)-2,-1,-1):
diff= max_prices_right - prices[i]
if prices[i]>max_prices_right:
max_prices_right=prices[i]
if diff> res_right[0]:
res_right.insert(0,diff)
else:
res_right.insert(0,res_right[0])
res=max(res_left[-1],res_right[0])
for i in range(len(prices)-1):
if res_left[i]+res_right[i+1]>res:
res=res_left[i]+res_right[i+1]
return res
188. 买卖股票的最佳时机 IV
#状态转移方程
#共有两个变量,第几天,共交易几次。
#dp[i][k][0]表示第i天,交易k次,当天手里不持有股票
#dp[i][k][1]表示第i天,交易k次,当天手里持有股票,
#最后的结果是dp[i最大值][k最大值][0],最后肯定是股票出手的状态
#递推公式
#前一天k次交易,今天无操作,前一天k次交易,今天卖出
#dp[i][k][0]=dp[i-1][k][0],dp[i-1][k][1]+prices[i]
#前一天k次交易,今天无操作,前一天k-1次交易,今天买入。买入操作才会对k加1
#dp[i][k][1]=dp[i-1][k][1],dp[i-1][k-1][0]-prices[i]
class Solution:
def maxProfit(self, k, prices):
if not prices:
return 0
n = len(prices)
max_k = n//2 # 最大交易次数
if k >= max_k: # k>最大交易次数,做 k=无穷大处理
res = 0
for i in range(n-1):
res += max(0, prices[i+1]-prices[i])
return res
else:
max_k = k
# k<最大交易次数,动态规划
dp = [[[0]*2 for _ in range(k+1)] for _ in range(n)]
#初始化
dp[0][0][1]=-float('inf')
for i in range(1,max_k+1):
dp[0][i][1] = -prices[0]
for i in range(1, n):
for k in range(1, max_k+1):
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1]+prices[i])
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0]-prices[i])
return dp[n-1][max_k][0]
k=2
prices=[3,2,6,5,0,3]
n=len(prices)
dp=[[[0]*2 for _ in range(k+1)] for _ in range(n)]
print(dp)
#初始化
dp[0][0][1]=-float('inf')
for i in range(1,k+1):
dp[0][i][1] = -prices[0]
for i in range(1,n):
for k in range(1,k+1):
dp[i][k][0]=max(dp[i-1][k][0],dp[i-1][k][1]+prices[i])
dp[i][k][1]=max(dp[i-1][k][1],dp[i-1][k-1][0]-prices[i])
print(dp)
print(dp[n-1][k][0])
动态规划心得
首先,动态规划问题的一般形式就是求最值。动态规划其实是运筹学的一种最优化方法,只不过在计算机问题上应用比较多,比如说让你求最长递增子序列呀,最小编辑距离呀等等。
既然是要求最值,核心问题是什么呢?求解动态规划的核心问题是穷举。因为要求最值,肯定要把所有可行的答案穷举出来,然后在其中找最值呗。
股票总结
通用思路:
LeetCode:121. 买卖股票的最佳时机(python)_wk的博客-CSDN博客_买卖股票的最佳时机python
第一题和第二题不用套公式,第一题直接遍历,第二题贪心就行了。其他的题目要套一下公式,注意单变量优化,k值过大时候的问题。
注意对比,买卖股票2(无次数限制)和买卖股票(无次数限制,有冷冻期)的差别,主要是一个是在单变量dp的时候,一个记录的是前一天的状态,一个记录的是前2天的状态。
121. 买卖股票的最佳时机
只能一次买入,这个题可以不套用模板做。
122. 买卖股票的最佳时机 II
与第一题不同的是,第二题是无限多次交易。可套用模板,将模板中的k去掉,因为k为无穷大,不受k的影响。
单变量优化。
class Solution:
def maxProfit(self, prices):
if not prices:
return 0
dp_i_0, dp_i_1 = 0, float('-inf')
for i in range(len(prices)):
temp = dp_i_0
dp_i_0 = max(dp_i_0, dp_i_1 + prices[i])
dp_i_1 = max(dp_i_1, temp - prices[i])
return dp_i_0
不套用模板更简单,贪心法:
class Solution(object):
def maxProfit(self, prices):
"""
:type prices: List[int]
:rtype: int
"""
profit = 0
for i in range(1, len(prices)):
tmp = prices[i] - prices[i - 1]
if tmp > 0: profit += tmp
return profit
123. 买卖股票的最佳时机 III
套公式。
class Solution:
def maxProfit(self, prices):
if not prices:
return 0
n = len(prices)
# 初始化状态
dp = [[[0]*2 for _ in range(3)] for _ in range(n)]
for k in range(3):
dp[0][k][1] = -prices[0]
# 从 i=1 处开始迭代
for i in range(1, n):
for k in range(1, 3):
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1]+prices[i])
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0]-prices[i])
return dp[n-1][2][0]
class Solution(object):
def maxProfit(self, prices):
if not prices:
return 0
n = len(prices)
def dfs(index,status,k):
# 递归终止条件,数组执行到头了,或者交易了两次了
if index==n or k==2:
return 0
# 定义三个变量,分别记录[不动]、[买]、[卖]
a,b,c = 0,0,0
# 保持不动
a = dfs(index+1,status,k)
if status:
# 递归处理卖的情况,这里需要将k+1,表示执行了一次交易
b = dfs(index+1,0,k+1)+prices[index]
else:
# 递归处理买的情况
c = dfs(index+1,1,k)-prices[index]
# 最终结果就是三个变量中的最大值
return max(a,b,c)
return dfs(0,0,0)
作者:wang_ni_ma
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/solution/wu-chong-shi-xian-xiang-xi-tu-jie-123mai-mai-gu-pi/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
status表示的是当前有没有持有股票。
188. 买卖股票的最佳时机 IV
套公式。
class Solution:
def maxProfit(self, k, prices):
if not prices:
return 0
n = len(prices)
max_k = n//2 # 最大交易次数
if k >= max_k: # k>最大交易次数,做 k=无穷大处理
res = 0
for i in range(n-1):
res += max(0, prices[i+1]-prices[i])
return res
else:
max_k = k
# k<最大交易次数,动态规划
dp = [[[0]*2 for _ in range(k+1)] for _ in range(n)]
for i in range(max_k+1):
dp[0][i][1] = -prices[0]
for i in range(1, n):
for k in range(1, max_k+1):
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1]+prices[i])
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0]-prices[i])
return dp[n-1][max_k][0]
309. 最佳买卖股票时机含冷冻期
套公式。
单变量优化
class Solution:
def maxProfit(self, prices):
if not prices:
return 0
# 初始化状态
dp_i_0, dp_i_1 = 0, float('-inf')
# 初始化前 2 天未持有股票状态的收益
dp_pre_0 = 0
# 等同于 i=0 处开始迭代
for i in range(len(prices)):
# 记录前 1 天状态
temp = dp_i_0
# 更新当天状态
dp_i_0 = max(dp_i_0, dp_i_1+prices[i])
dp_i_1 = max(dp_i_1, dp_pre_0-prices[i])
# 更新记录的前 1 天状态为前 2 天状态
dp_pre_0 = temp
return dp_i_0
01背包
问题描述
状态转移方程
空间优化
空间优化要倒叙遍历,因为,要保证f[v-c[i]]是在i-1状态的,v- c[i]比v小,如果正序遍历,则都是当前i状态的值,
倒叙遍历能保证,更新f[v]的时候,f[v-c[i]]是上一个状态更新的。
完全背包
状态转移方程和空间优化
零钱兑换
零钱兑换1,完全背包问题
dp[v]表示体积为v的时候,results是多少
class Solution(object):
def coinChange(self, coins, amount):
"""
:type coins: List[int]
:type amount: int
:rtype: int
"""
#前i个物品,放入容量为v的背包的最大价值
#前i个物品,放入容量为v的背包,并且把背包塞满,的组合数。
#当前状态,等于,上一个状态(i-1)个物品,当前物品不放,则没有数量的加减
#如果当前物品放,则有数量的加减
#dp[i][j]=min(dp[i-1][j],dp[i-1][j-c[i]]+1),通过状态转移方程,
#可看出这是完全背包问题,直接套完全背包的公式。与完全背包不同的是,这个题的初始状态要设置为最大值
dp=[float('inf') for _ in range(amount+1)]
dp[0]=0
for i in range(len(coins)):
for j in range(1,amount+1):
if j>=coins[i]:
dp[j]=min(dp[j],dp[j-coins[i]]+1)
if dp[-1]!=float('inf'):
return dp[-1]
return -1
零钱兑换2
也是完全背包问题
class Solution(object):
def change(self, amount, coins):
"""
:type amount: int
:type coins: List[int]
:rtype: int
"""
dp=[0 for _ in range(amount+1)]
dp[0]=1
for i in range(len(coins)):
for j in range(1,amount+1):
if j>=coins[i]:
dp[j]=dp[j]+dp[j-coins[i]]
return dp[-1]
组合总和
零钱兑换2实际上就是组合总和问题,以前组合组合用的是回溯法解决,这次用完全背包法,仔细体会两者复杂度的区别!!
72.编辑距离-字符串的动态规划
class Solution:
def minDistance(self, word1, word2):
"""
:type word1: str
:type word2: str
:rtype: int
"""
n = len(word1)
m = len(word2)
# 有一个字符串为空串
if n * m == 0:
return n + m
# DP 数组
D = [ [0] * (m + 1) for _ in range(n + 1)]
# 边界状态初始化
for i in range(n + 1):
D[i][0] = i
for j in range(m + 1):
D[0][j] = j
# 计算所有 DP 值
for i in range(1, n + 1):
for j in range(1, m + 1):
left = D[i - 1][j] + 1
down = D[i][j - 1] + 1
left_down = D[i - 1][j - 1]
if word1[i - 1] != word2[j - 1]:
left_down += 1
D[i][j] = min(left, down, left_down)
return D[n][m]
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/edit-distance/solution/bian-ji-ju-chi-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
887. 鸡蛋掉落
解法1
暴力递归的方法。
class Solution:
def superEggDrop(self, K, N):
memo = {}
def dp(k, n):
if (k, n) not in memo:
if n == 0:
ans = 0
elif k == 1:
ans = n
else:
lo, hi = 1, n
# keep a gap of 2 X values to manually check later
while lo + 1 < hi:
x = (lo + hi) // 2
t1 = dp(k-1, x-1)
t2 = dp(k, n-x)
if t1 < t2:
lo = x
elif t1 > t2:
hi = x
else:
lo = hi = x
ans = 1 + min(max(dp(k-1, x-1), dp(k, n-x))
for x in (lo, hi))
memo[k, n] = ans
#如果这个k n计算过了,直接返回就行。
return memo[k, n]
return dp(K, N)
312. 戳气球
打家劫舍
198. 打家劫舍
class Solution:
def rob(self, nums: List[int]) -> int:
if not nums:
return 0
size = len(nums)
if size == 1:
return nums[0]
dp = [0] * size
dp[0] = nums[0]
dp[1] = max(nums[0], nums[1])
for i in range(2, size):
dp[i] = max(dp[i - 2] + nums[i], dp[i - 1])
return dp[size - 1]
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/house-robber/solution/da-jia-jie-she-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。