视频来源:https://www.bilibili.com/video/BV1xb411e7ww?from=search&seid=3112459103674479435
1.1 最后一步
k枚硬币,面值加起来应该等于27,最后的硬币是
除掉最后一枚硬币,前面的k-1枚硬币加起来应该等于27-,因为是最优策略,所以拼出27-的硬币数量也最少,为了简化定义,我们设状态F[x]=最少用多少枚硬币拼出x
什么时候需要人工定义F[0]? 用转移方程算不出来的时候
有3种硬币,其面值为2元、5元、7元,现在要拼成27元,并要求硬币数量最少
备注:每种硬币数量都无穷多
import pandas
step_coin_select=[]
coins = [2,5,7] #硬币种类面值
amount = 27 #拼出的金额
f=[0]*(amount+1) #f[0]=0为初始条件
#i 为f(1) f(2) f(3)...f(11)
for i in range(1,amount+1):
# 设定f(i)初始值为正无穷,为拼不出的金额做准备
f[i]=float("inf")
for j in range(len(coins)):
#如果金额小于某种硬币面值
if i>=coins[j]:
f[i]=min(f[i],(f[i-coins[j]]+1))
if f[i]==(f[i-coins[j]]+1):
temp=['f[{}]'.format(i),(i-coins[j]),coins[j],f[i]]
step_coin_select.append(temp)
step_coin_selectdata=pandas.DataFrame(step_coin_select,columns=['金额','子问题','最后状态','最少硬币数'])
step_coin_selectdata=step_coin_selectdata.sort_values(by=['金额','硬币数'],ascending=[1,1])#0降序
#去重
step_coin_select_dup=step_coin_selectdata.drop_duplicates(['金额'])
给定m行n列的网格,有一个机器人从网格[0,0]处出发,且只能往右或往下走,有多少条路径可以走到右下角?
1)动态规划组成一:状态
设f[i,j]为有多少种方式从左上角走到[i,j]
1.1)最后状态:[m-1,n-1]
1.2)子问题:走到最后状态[m-1,n-1]的前一步是[m-2,n-1]或者[m-1,n-2]
设有X步从左上角走到[m-2,n-1],有Y步从左上角走到[m-1,n-2],那么从左上角走到右下角的方式有X+Y种
2)动态规划组成二:转移方程
f[i,j]=f[i-1,j]+[i,j-1],f[i,j]为有多少种方式从左上角走到[i,j]
3)动态规划组成三:初始条件和边界情况
f[0,0]=1 #机器人只要一种方式进入左上角
边界情况:
i=0 或者 j=0时,f[i,j]只要一种方式可以过来
4)动态规划组成四:计算顺序
f[0,0]=1
然后计算第一行
计算第二行
...
计算m-1行
多少条路径答案:f[m-1,n-1]
时间复杂度(计算步数):O(MN),空间复杂度:O(MN)
"""
https://leetcode-cn.com/problems/unique-paths/%20/
给定m行n列的网格,有一个机器人从网格[0,0]处出发,且只能往右或往下走,有多少条路径可以走到右下角?
1、状态:
1.1 最后一步:[m-1,n-1]
1.2 子问题:走到最后状态[m-1,n-1]的前一步是[m-2,n-1]或者[m-1,n-2]
设有X步从左上角走到[m-2,n-1],有Y步从左上角走到[m-1,n-2],那么从左上角走到右下角的方式有X+Y种
2、动态规划组成二:转移方程:
f[i,j]=f[i-1,j]+f[i,j-1]
3、初始条件和边界情况:
f[0,0]=1 #机器人只要一种方式进入左上角
边界情况:i=0 或者 j=0时,f[i,j]只要一种方式可以过来
4、计算顺序:从上到下,从左到右
results=[[1]*n]*m遇到bug,
原因是list的浅拷贝问题
list * n—>n shallow copies of list concatenated
n个list的浅拷贝的连接
修改其中的任何一个元素会改变整个列表
改写为循环赋值即可[([0]*n) for i in range(n)]
"""
n=7
m=8
results=[[1]*n for i in range(m)]
#f[0,j]和f[i,0]的路径显然只有1条,所以跳过
for i in range(1,m):
for j in range(1,n):
results[i][j]=results[i-1][j]+results[i][j-1]
print(i,j,results[i][j])
print("-"*30)
# 总共有多少条不同的路径?
print(results[m-1][n-1])
# leetcode;
# class Solution:
# def uniquePaths(self, m: int, n: int) -> int:
# results=[[1]*n for i in range(m)]
# #f[0,j]和f[i,0]的路径显然只有1条,所以跳过
# for i in range(1,m):
# for j in range(1,n):
# results[i][j]=results[i-1][j]+results[i][j-1]
# return results[m-1][n-1]
#/*-------------------------------------*/
#/* jump-game
#https://leetcode.com/problems/jump-game/
#/*-------------------------------------*/
a=[2,3,1,1,4]
a=[3,2,1,0,4]
a=[2,0,0]
n=len(a)
flag=True
for i in range(n-1,0,-1):
#flag为True有两种情况,一个是初始情况最后一步的前一步可以跳到最后一步,一种是上一次的j可以跳到i
if flag:
#寻找是否可以从第j步跳到i 步
for j in range(i-1,-1,-1):
print(i,j,"比较:",a[j]>=(i-j))
#如果j可以跳到i
if (a[j]>=(i-j)):
i=j
flag=True
break
else:
flag=False
print("-"*30)
# flag: Determine if you are able to reach the last index.
print(flag)
# leetcode
# class Solution:
# def canJump(self, nums: List[int]) -> bool:
# global flag
# n=len(nums)
# flag=True
# for i in range(n-1,0,-1):
# #flag为True有2种情况,一个是初始情况最后一步的前一步可以跳到最后一步,一种是上一次的j可以跳到i
# if flag:
# #寻找是否可以从第j步跳到i 步
# for j in range(i-1,-1,-1):
# #如果j可以跳到i
# if (nums[j]>=(i-j)):
# i=j
# flag=True
# break
# else:
# flag=False
# return flag
在M件物品取出若干件放在体积为W的背包里,每件物品只有一件,他们有各自的体积和价值,问如何选择使得背包能够装下的物品价值最多
动态规划的思路:一个一个物品去尝试,一点一点扩大考虑能够容纳的容积的大小,整个过程就像在填写一张二维表格
创建一个dp[M+1,W+1]的二维数据,代表其能够装下的最大价值,其中第一行全是0,代表没有物品放进来,第一列全是0,代表不放东西
容量 | |||||||||||||||
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | ||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ||
物品重量 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
2 | 0 | 1 | 6 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | 7 | |
5 | 0 | 1 | 6 | 7 | 7 | 18 | 19 | 24 | 25 | 25 | 25 | 25 | 25 | 25 | |
6 | 0 | 1 | 6 | 7 | 7 | 18 | 22 | 24 | 28 | 29 | 29 | 40 | 41 | 46 | |
7 | 0 | 1 | 6 | 7 | 7 | 18 | 22 | 28 | 29 | 34 | 35 | 40 | 46 | 50 | |
9 | 0 | 1 | 6 | 7 | 7 | 18 | 22 | 28 | 29 | 36 | 37 | 42 | 46 | 50 |
import numpy as np
weights=[1,2,5,6,7,9]
price=[1,6,18,22,28,36]
n=len(weights)
print("n:",n) #6
W=13
dp=np.array([[0]*(W+1)]*(n+1))
# 计算顺序:从上往下,一行一行计算
#r代表某件物品
for r in range(1,n+1):
for c in range(1,W+1):
#如果第n件物品大于容量c,那么在该容量下放不下该物品,最佳价值等于该容量下前n-1的最大价值
if weights[r-1]>c:
dp[r,c]=dp[r-1,c]
#该容量下能放下该物品,有两种选项,放和不放
#1、放:放下该物品对应的价值price[r-1]加上放下该物品后剩余容量对应的最大价值dp[r-1,c-weights[r-1]]
#2、不放:前n-1的最大价值dp[r-1,c]
else:
dp[r,c]=max(dp[r-1,c],dp[r-1,c-weights[r-1]]+price[r-1])
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
每个数组中的元素不会超过 100
数组的大小不会超过 200
示例 1:
输入: [1, 5, 11, 5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].
解题思路从0-1背包而来,只是dp[r,c]代表的是在前r个nums有没有和为c,有的话为True,没有的话False
nums=[3,3,3,4,5]
n=len(nums)+1
half=sum(nums)/2+1
half=int(half)
dp=np.array([[0]*half]*n)
for r in range(1,n):
for c in range(1,half):
print(r,c)
if nums[r-1]==c or dp[r-1,c]==1:
dp[r,c]=1
elif c-nums[r-1]>0 and dp[r-1,c-nums[r-1]]==1:
dp[r,c]=1
else:
pass
import pandas
col=[]
for i in range(half):
col.append("a{}".format(i))
dp_data=pandas.DataFrame(dp,columns=col)
# leetcode
# import numpy as np
# class Solution:
# def canPartition(self, nums: List[int]) -> bool:
# n=len(nums)+1
# half=sum(nums)/2+1
# if half%1==0 and n>2:
# half=int(half)
# dp=np.array([[0]*half]*n)
# for r in range(1,n):
# for c in range(1,half):
# if nums[r-1]==c or dp[r-1,c]==1:
# dp[r,c]=1
# elif c-nums[r-1]>0 and dp[r-1,c-nums[r-1]]==1:
# dp[r,c]=1
# else:
# pass
# else:
# return False
# return dp[r,c]==1
话说你有一个数组,这个数组代表每天股票的交易价格
现在只允许你买卖一次,即只能买一次卖一次,设计一个算法去寻找最大收益
注意你不能在买入之前卖出。
原题:https://leetcode.com/problems/best-time-to-buy-and-sell-stock/
思路:利用动态规划的思想迭代最小价格和收益
prices=[2,1,2,0,2]
min_value=max(prices)
profit=0
for v in prices:
if vprofit:
profit=v-min_value
print(profit)
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
prices=[2,1,2,0,1]
length = len(prices)
max_profit, buy, sell = 0, -1, -1
for i in range(length):
if i==length-1:
pass
elif prices[i] int:
# max_profit, buy, sell = 0, -1, -1
# length = len(prices)
# if length>0:
# for i in range(length):
# if i==length-1:
# pass
# elif prices[i]
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
对问题进行定义:dp[i][k][status]表示累计最大利润,其中i表示第i天,k表示已经完成几次买卖交易,status表示状态(0卖出,1买入)
dp[i][0][0]:表示第i天交易了0次时卖出后的累计最大利润,对应于初始状态,因为没有买卖所以是0
dp[i][0][1]:表示第i天交易了0次时买入后的累计最大利润
dp[i][1][0]:表示第i天交易了1次时卖出后的累计最大利润
dp[i][1][1]:表示第i天交易了1次时买入后的累计最大利润
dp[i][2][0]:表示第i天交易了2次时卖出后的累计最大利润
dp[i][2][1]:表示第i天交易了2次时买入后的累计最大利润
注意,最后一个dp[i][2][1] 实际是不存在的,因为交易两次后,就不能再买入了。
我们定义第1次买卖的dp公式
第一次买入,根据昨天权衡后决定的买入和今天买入,对比取最大值
dp[i][0][1]=max(dp[i-1][0][1],-prices[i])
第一次卖出,可以根据昨天权衡后决定的卖出和今天卖出减去昨天权衡后的买入,对比取最大值
dp[i][1][0]=max(dp[i-1][1][0],prices[i]-dp[i-1][0][1])
第二次买入,根据昨天权衡后决定的买入和今天买入并加上第一次收益,对比取最大值
dp[i][1][1]=max(dp[i-1][1][1],dp[i-1][1][0]-prices[i])
第二次卖出,可以根据昨天权衡后决定的卖出和今天卖出减去昨天权衡后的买入,对比取最大值
dp[i][2][0]=max(dp[i-1][2][0],prices[i]-dp[i-1][1][1])
import numpy as np
prices=[7,1,5,3,6,4]
#买入卖出2种状态
status=2
#交易2次
k=2
#天数
n=len(prices)
dp=np.array([[[-1]*2]*(k+1) for _ in range(n)])
#设置初始状态
dp[0,0,0]=0 #初始状态,没买入没卖出
dp[0,0,1]=-prices[0] #第一天第1次买入收益为-7
dp[0,1,0]=0 #第一天第1次卖出收益为0,因为没有开始卖
dp[0,1,1]=-prices[0] #第一天第2次买入收益为-7
dp[0,2,0]=0 #第一天第2次卖出收益为0,因为没有开始卖
dp[0,2,1]=-prices[0]
for i in range(1,n):
dp[i,0,0]=0 #初始状态,没买入没卖出
#[i,0,1],第i天第1次买入累计最大收益,[i,1,0],第i天第1次卖出累计最大收益
dp[i,0,1]=max(dp[i-1,0,1],-prices[i])
dp[i,1,0]=max(dp[i-1,1,0],prices[i]+dp[i-1,0,1])
#[i,1,1],第i天第2次买入累计最大收益,[i,2,0],第i天第2次卖出累计最大收益
dp[i,1,1]=max(dp[i-1,1,1],dp[i,1,0]-prices[i])
dp[i,2,0]=max(dp[i-1,2,0],prices[i]+dp[i-1,1,1])
#所能获取的最大利润
max(dp[n-1,1,0],dp[n-1,2,0])