详解动态规划算法(Python)
视频来源:https://www.bilibili.com/video/BV1xb411e7ww?from=search&seid=3112459103674479435
动态规划解题四组成部分
1、确定状态
解动态规划的时候需要一个数组,数组的每个元素F[i],或者F[i,j]代表什么需要明确;
确定状态需要两个意识:
1.1 最后一步
k枚硬币,面值加起来应该等于27,最后的硬币是
1.2 子问题
除掉最后一枚硬币,前面的k-1枚硬币加起来应该等于27-,因为是最优策略,所以拼出27-
的硬币数量也最少,为了简化定义,我们设状态F[x]=最少用多少枚硬币拼出x
2、建立状态转移方程
3、初始条件和边界情况
什么时候需要人工定义F[0]? 用转移方程算不出来的时候
![](/assets/blank.gif)
4、计算顺序
5、 例子
5.1:最少硬币数
有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(['金额'])
5.2:不同路径
给定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]
5.3:jump game
#/*-------------------------------------*/
#/* 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可以跳到iif flag:#寻找是否可以从第j步跳到i 步for j in range(i-1,-1,-1):print(i,j,"比较:",a[j]>=(i-j))#如果j可以跳到iif (a[j]>=(i-j)):i=jflag=Truebreakelse:flag=Falseprint("-"*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
5.4 0-1背包
在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=13dp=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])
5.5 分割等和子集(416)
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
每个数组中的元素不会超过 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+1half=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]=1elif c-nums[r-1]>0 and dp[r-1,c-nums[r-1]]==1:dp[r,c]=1else: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
5.6 买卖股票最佳时机
话说你有一个数组,这个数组代表每天股票的交易价格
现在只允许你买卖一次,即只能买一次卖一次,设计一个算法去寻找最大收益
注意你不能在买入之前卖出。
原题: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 v<min_value:min_value=vif v-min_value>profit:profit=v-min_valueprint(profit)
5.7 买卖股票的最佳时机 II (122题)
给定一个数组,它的第 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:passelif prices[i]<prices[i+1]:buy=prices[i]sell=prices[i+1]max_profit+=sell-buyelse:pass
print(max_profit)#leetcode answer
# class Solution:
# def maxProfit(self, prices: List[int]) -> 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]<prices[i+1]:
# buy=prices[i]
# sell=prices[i+1]
# max_profit+=sell-buy
# else:
# pass
# return max_profit
5.8 买卖股票的最佳时机 III (123)
给定一个数组,它的第 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])
详解动态规划算法(Python)相关推荐
- 详解动态规划算法(Python实现动态规划算法典型例题)
动态规划(Dynamic programming) 是一种在数学.计算机科学和经济学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法. 动态规划算法是通过拆分问题,定义问题状态和状 ...
- 【机器学习】【隐马尔可夫模型-3】后向算法:算法详解+示例讲解+Python实现
0.前排提示 csdn有些数学公式编辑不出来,所以本博用容易书写的表达式来表示专业数学公式,如: (1) 在本博客中用α<T>(i)来表示 (2)在本博客中用[i=1, N]∑来表示 注 ...
- 详解线性回归算法的纯Python实现
↑↑↑关注后"星标"简说Python人人都可以简单入门Python.爬虫.数据分析 简说Python推荐 来源|天池大数据科研平台作者|黄佳 零基础学机器学习--一文详解线性回归算 ...
- python实现连续变量最优分箱详解--CART算法
今天小编就为大家分享一篇python实现连续变量最优分箱详解–CART算法,具有很好的参考价值,希望对大家有所帮助.一起跟随小编过来看看吧 关于变量分箱主要分为两大类:有监督型和无监督型 对应的分箱方 ...
- 详解floyd算法 及<MATLAB>实现
欢迎来到 < Haoh-Smile > 的博客,觉得受用客官就点个赞评论一下呗!** 详解floyd算法 及MATLAB实现 一.Floyd算法原理 Floyd算法是一个经典的动态规划算法 ...
- 详解动态规划最长公共子序列--JavaScript实现
前面两篇我们讲解了01背包问题和最少硬币找零问题.这篇将介绍另一个经典的动态规划问题--最长公共子序列.如果没看过前两篇,可点击下面链接. 详解动态规划最少硬币找零问题--JavaScript实现 详 ...
- python英语字典程序修改_详解如何修改python中字典的键和值
我们知道python中字典是无序的,它们都是通过hash去对应的.一般的如果我们需要修改字典的值,只需要直接覆盖即可,而修改字典的键,则需要使用字典自带的pop函数,示例如下: t = {} t['a ...
- 详解rsync算法--如何减少同步文件时的网络传输量
详解rsync算法--如何减少同步文件时的网络传输量 先看下图中的场景,客户端A和B,以及服务器server都保存了同一个文件,最初,A.B和server上的文件内容都是相同的(记为File.1).某 ...
- java动态规划凑硬币问题,详解动态规划最少硬币找零问题--JavaScript实现
硬币找零问题是动态规划的一个经典问题,其中最少硬币找零是一个变种,本篇将参照上一篇01背包问题的解题思路,来详细讲解一下最少硬币找零问题.如果你需要查看上一篇,可以点击下面链接: 详解动态规划01背包 ...
最新文章
- C++ 笔记(36)—— std::cout 输出保留小数位数
- 阿里发布AliGenie2.0系统,“百箱大战”用上视觉武器
- MySQL性能优化、故障排查及最佳实践秘籍,阿里云数据库专家玄惭的“武功”全记录...
- 图解opengl曲线和曲面绘制
- 从来富贵险中求 为何低学历的人能成为亿万富翁
- TCP/IP学习笔记(四)TCP超时重传及拥塞控制
- 若川知乎问答:做前端感觉很吃力怎么办?
- 使用try-with-resources优雅的关闭IO流
- 微型计算机接口与技术答案,微型计算机接口技术与应用习题答案(刘乐善).doc
- 七日年化收益率计算器_定投收益率该怎么算?
- 三种最常用的日志分析软件
- 基于深度学习的帧内预测技术
- mysql高性能学习笔记03_【MySQL】《高性能MySQL》 学习笔记,第三章,服务器性能剖析...
- Lightoj1009 Back to Underworld(带权并查集)
- Android屏幕适配全方位解析与指导
- JavaScript期末大作业 罗兰永恒花园动漫价绍网页 7页,含有table表格,js表单验证还有首页视频
- input_dim、input_length的理解
- 《融智学进阶文集》01:间接计算模型和间接形式化方法
- STM32串口中 USART_GetITStatus 与 USART_GetFlagStatus的区别
- Linux(12)Debain系统安装远程控制软件