视频来源: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]?  用转移方程算不出来的时候


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)相关推荐

  1. 详解动态规划算法(Python实现动态规划算法典型例题)

    动态规划(Dynamic programming) 是一种在数学.计算机科学和经济学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法. 动态规划算法是通过拆分问题,定义问题状态和状 ...

  2. 【机器学习】【隐马尔可夫模型-3】后向算法:算法详解+示例讲解+Python实现

    0.前排提示 csdn有些数学公式编辑不出来,所以本博用容易书写的表达式来表示专业数学公式,如: (1)  在本博客中用α<T>(i)来表示 (2)在本博客中用[i=1, N]∑来表示 注 ...

  3. 详解线性回归算法的纯Python实现

    ↑↑↑关注后"星标"简说Python人人都可以简单入门Python.爬虫.数据分析 简说Python推荐 来源|天池大数据科研平台作者|黄佳 零基础学机器学习--一文详解线性回归算 ...

  4. python实现连续变量最优分箱详解--CART算法

    今天小编就为大家分享一篇python实现连续变量最优分箱详解–CART算法,具有很好的参考价值,希望对大家有所帮助.一起跟随小编过来看看吧 关于变量分箱主要分为两大类:有监督型和无监督型 对应的分箱方 ...

  5. 详解floyd算法 及<MATLAB>实现

    欢迎来到 < Haoh-Smile > 的博客,觉得受用客官就点个赞评论一下呗!** 详解floyd算法 及MATLAB实现 一.Floyd算法原理 Floyd算法是一个经典的动态规划算法 ...

  6. 详解动态规划最长公共子序列--JavaScript实现

    前面两篇我们讲解了01背包问题和最少硬币找零问题.这篇将介绍另一个经典的动态规划问题--最长公共子序列.如果没看过前两篇,可点击下面链接. 详解动态规划最少硬币找零问题--JavaScript实现 详 ...

  7. python英语字典程序修改_详解如何修改python中字典的键和值

    我们知道python中字典是无序的,它们都是通过hash去对应的.一般的如果我们需要修改字典的值,只需要直接覆盖即可,而修改字典的键,则需要使用字典自带的pop函数,示例如下: t = {} t['a ...

  8. 详解rsync算法--如何减少同步文件时的网络传输量

    详解rsync算法--如何减少同步文件时的网络传输量 先看下图中的场景,客户端A和B,以及服务器server都保存了同一个文件,最初,A.B和server上的文件内容都是相同的(记为File.1).某 ...

  9. java动态规划凑硬币问题,详解动态规划最少硬币找零问题--JavaScript实现

    硬币找零问题是动态规划的一个经典问题,其中最少硬币找零是一个变种,本篇将参照上一篇01背包问题的解题思路,来详细讲解一下最少硬币找零问题.如果你需要查看上一篇,可以点击下面链接: 详解动态规划01背包 ...

最新文章

  1. C++ 笔记(36)—— std::cout 输出保留小数位数
  2. 阿里发布AliGenie2.0系统,“百箱大战”用上视觉武器
  3. MySQL性能优化、故障排查及最佳实践秘籍,阿里云数据库专家玄惭的“武功”全记录...
  4. 图解opengl曲线和曲面绘制
  5. 从来富贵险中求 为何低学历的人能成为亿万富翁
  6. TCP/IP学习笔记(四)TCP超时重传及拥塞控制
  7. 若川知乎问答:做前端感觉很吃力怎么办?
  8. 使用try-with-resources优雅的关闭IO流
  9. 微型计算机接口与技术答案,微型计算机接口技术与应用习题答案(刘乐善).doc
  10. 七日年化收益率计算器_定投收益率该怎么算?
  11. 三种最常用的日志分析软件
  12. 基于深度学习的帧内预测技术
  13. mysql高性能学习笔记03_【MySQL】《高性能MySQL》 学习笔记,第三章,服务器性能剖析...
  14. Lightoj1009 Back to Underworld(带权并查集)
  15. Android屏幕适配全方位解析与指导
  16. JavaScript期末大作业 罗兰永恒花园动漫价绍网页 7页,含有table表格,js表单验证还有首页视频
  17. input_dim、input_length的理解
  18. 《融智学进阶文集》01:间接计算模型和间接形式化方法
  19. STM32串口中 USART_GetITStatus 与 USART_GetFlagStatus的区别
  20. Linux(12)Debain系统安装远程控制软件

热门文章

  1. Parcelable与Serializable
  2. java后台向前台输出弹出框
  3. JS合并数组中id相同的项目 去重合并
  4. Iframe自适应高度,Iframe高度问题解决
  5. Python爬虫练习笔记——爬取单个网页里的所有图片(入门)
  6. 靠谱的pyCharm最新2018激活码
  7. CALayer的使用(圆形头像 锚点 边框 阴影 形变 隐式动画 )
  8. 阿里飞冰 Img组件绑定事件无效
  9. 窗口看门狗与编程实验
  10. java set循环取值_Java遍历Map和遍历Set