今天整理了一下关于动态规划的内容,道理都知道,但是python来描述的方面参考较少,整理如下,希望对你有所帮助,实验代码均经过测试。

请先好好阅读如下内容--什么是动态规划?

摘录于《算法图解》

这里写图片描述

以上的都建议自己手推一下,然后知道怎么回事,核心的部分是142页核心公式,待会代码会重现这个过程,推荐没有算法基础的小伙伴看这本书《算法图解》很有意思的书,讲的很清晰,入门足够

更深入的请阅读python算法-动态规划写的不错,可以参考

为什么要使用动态规划?

​ 首先我们要知道为什么要使用(Dynamic programming)dp,我们在选择dp算法的时候,往往是在决策问题上,而且是在如果不使用dp,直接暴力效率会很低的情况下选择使用dp.

那么问题来了,什么时候会选择使用dp呢,一般情况下,我们能将问题抽象出来,并且问题满足无后效性,满足最优子结构,并且能明确的找出状态转移方程的话,dp无疑是很好的选择。

无后效性通俗的说就是只要我们得出了当前状态,而不用管这个状态怎么来的,也就是说之前的状态已经用不着了,如果我们抽象出的状态有后效性,很简单,我们只用把这个值加入到状态的表示中。

最优子结构(自下而上):在决策问题中,如果,当前问题可以拆分为多个子问题,并且依赖于这些子问题,那么我们称为此问题符合子结构,而若当前状态可以由某个阶段的某个或某些状态直接得到,那么就符合最优子结构

重叠子问题(自上而下):动态规划算法总是充分利用重叠子问题,通过每个子问题只解一次,把解保存在一个需要时就可以查看的表中,每次查表的时间为常数,如备忘录的递归方法。斐波那契数列的递归就是个很好的例子

状态转移:这个概念比较简单,在抽象出上述两点的的状态表示后,每种状态之间转移时值或者参数的变化。

小结

动态规划: 动态规划表面上很难,其实存在很简单的套路:当求解的问题满足以下两个条件时, 就应该使用动态规划:

主问题的答案 包含了 可分解的子问题答案 (也就是说,问题可以被递归的思想求解)

递归求解时, 很多子问题的答案会被多次重复利用

动态规划的本质思想就是递归, 但如果直接应用递归方法, 子问题的答案会被重复计算产生浪费, 同时递归更加耗费栈内存, 所以通常用一个二维矩阵(表格)来表示不同子问题的答案, 以实现更加高效的求解。

Talk is cheap ,Show me the code

翻阅很多资料,貌似python描述的比较少,这里总结一下,用前面的图解中的伪代码重构下

背包问题

# 这里使用了图解中的吉他,音箱,电脑,手机做的测试,数据保持一致

w = [0, 1, 4, 3, 1] #n个物体的重量(w[0]无用)

p = [0, 1500, 3000, 2000, 2000] #n个物体的价值(p[0]无用)

n = len(w) - 1 #计算n的个数

m = 4 #背包的载重量

x = [] #装入背包的物体,元素为True时,对应物体被装入(x[0]无用)

v = 0

#optp[i][j]表示在前i个物体中,能够装入载重量为j的背包中的物体的最大价值

optp = [[0 for col in range(m + 1)] for raw in range(n + 1)]

#optp 相当于做了一个n*m的全零矩阵的赶脚,n行为物件,m列为自背包载重量

def knapsack_dynamic(w, p, n, m, x):

#计算optp[i][j]

for i in range(1, n + 1): # 物品一件件来

for j in range(1, m + 1): # j为子背包的载重量,寻找能够承载物品的子背包

if (j >= w[i]): # 当物品的重量小于背包能够承受的载重量的时候,才考虑能不能放进去

optp[i][j] = max(optp[i - 1][j], optp[i - 1][j - w[i]] + p[i]) # optp[i - 1][j]是上一个单元的值, optp[i - 1][j - w[i]]为剩余空间的价值

else:

optp[i][j] = optp[i - 1][j]

#递推装入背包的物体,寻找跳变的地方,从最后结果开始逆推

j = m

for i in range(n, 0, -1):

if optp[i][j] > optp[i - 1][j]:

x.append(i)

j = j - w[i]

#返回最大价值,即表格中最后一行最后一列的值

v = optp[n][m]

return v

print '最大值为:' + str(knapsack_dynamic(w, p, n, m, x))

print '物品的索引:',x

#最大值为:4000

#物品的索引: [4, 3]

优化背包问题的递归方法

def MaxVal2(memo , w, v, index, last):

"""

得到最大价值

w为widght

v为value

index为索引

last为剩余重量

"""

global numCount

numCount = numCount + 1

try:

#以往是否计算过分支,如果计算过,直接返回分支的结果

return memo[(index , last)]

except:

#最底部

if index == 0:

#是否可以装入

if w[index] <= last:

return v[index]

else:

return 0

#寻找可以装入的分支

without_l = MaxVal2(memo , w, v, index - 1, last)

#如果当前的分支大于约束

#返回历史查找的最大值

if w[index] > last:

return without_l

else:

#当前分支加入背包,剪掉背包剩余重量,继续寻找

with_l = v[index] + MaxVal2(memo , w, v , index - 1, last - w[index])

#比较最大值

maxvalue = max(with_l , without_l)

#存储

memo[(index , last)] = maxvalue

return maxvalue

w = [0, 1, 4, 3, 1] # 东西的重量

v = [0, 1500, 3000, 2000, 2000] # 东西的价值

numCount = 0

memo = {}

n = len(w) - 1

m = 4

print MaxVal2(memo , w, v, n, m) , "caculate count : ", numCount

# 4000 caculate count : 20

优化斐波那契数列的递归方法

多谢Python科学实验----动态规划,也就是对应上面的重叠子问题的方法,备忘录的递归方法

#Dynamic Method Experiment

import matplotlib.pyplot as plt

count=0;

#blank

def f(n):

global count

count=count+1

if n==1:

return 1

elif n==0:

return 1

else:

return f(n-1)+f(n-2)

# function calls count

def calc_f(n):

global count

count=0

f(n)

return count

#using memorization

mem={}

def mem_f(n):

global count,mem

count=count+1

if n in mem:

return mem[n]

else:

if n==1:

result=1

elif n==0:

result=1

else:

result=mem_f(n-1)+mem_f(n-2)

mem[n]=result

return result

def mem_calc_f(n):

global count

global mem

mem={}

count=0

mem_f(n)

return count

x=range(1,15)

y=[]

y2=[]

for i in x:

c=mem_calc_f(i)

y.append(c)

c2=calc_f(i)

y2.append(c2)

print "规模为%d时计算了%d次 i=%d时,val=%d"%(i,c,i,mem_f(i))

print "规模为%d时计算了%d次 i=%d时,val=%d"%(i,c2,i,f(i))

plt.plot(x,y)

plt.plot(x,y2)

plt.show()

它的基本思想就是记录已经计算过的值,避免重复计算。

如果使用装饰器的写法,则会优雅很多

from functools import wraps

def memo(func):

cache={}

@wraps(func)

def wrap(*args):

if args not in cache:

cache[args]=func(*args)

return cache[args]

return wrap

@memo

def fib(i):

if i<2: return 1

return fib(i-1)+fib(i-2)

fib(2)

一些利用DP的笔试题

CPU双核问题

网易笔试—动态规划: 题目的大概意思:一种双核CPU的两个核能够同时的处理任务,现在有n个已知数据量的任务需要交给CPU处理,假设已知CPU的每个核1秒可以处理1kb,每个核同时只能处理一项任务。n个任务可以按照任意顺序放入CPU进行处理,现在需要设计一个方案让CPU处理完这批任务所需的时间最少,求这个最小的时间。

输入包括两行:

第一行为整数n(1 ≤ n ≤ 50)

第二行为n个整数length[i](1024 ≤ length[i] ≤ 4194304),表示每个任务的长度为length[i]kb,每个数均为1024的倍数。

输出一个整数,表示最少需要处理的时间。

问题实质是动态规划问题,把数组分成两部分,使得两部分的和相差最小。

如何将数组分成两部分使得两部分的和的差最小?参考博客http://www.tuicool.com/articles/ZF73Af

思路:

差值最小就是说两部分的和最接近,而且各部分的和与总和的一半也是最接近的。假设用sum1表示第一部分的和,sum2表示第二部分的和,SUM表示所有数的和,那么sum1+sum2=SUM。假设sum1

所以我们就有目标了,使得sum1<=SUM/2的条件下尽可能的大。也就是说从n个数中选出某些数,使得这些数的和尽可能的接近或者等于所有数的和的一般。这其实就是简单的背包问题了:

背包容量是SUM/2. 每个物体的体积是数的大小,然后尽可能的装满背包。

w = [0, 3072, 3072, 7168, 3072, 1024] # 假设进入处理的的任务大小

w = map(lambda x:x/1024,w) # 转化下

p = w # 这题的价值和任务重量一致

n = sum(w)/2 +1 # 背包承重为总任务的一半

optp = [[0 for j in range(n+1)] for i in range(len(w))]

for i in range(1,len(p)):

for j in range(1,n+1):

if j >= p[i]:

optp[i][j] = max(optp[i-1][j],p[i]+optp[i-1][j-w[i]])

else:

optp[i][j] = optp[i-1][j]

print optp[-1][-1]

print optp

# 背包矩阵入下所示,第一列和第一行无效占位符

[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],

[0, 0, 0, 3, 3, 3, 3, 3, 3, 3],

[0, 0, 0, 3, 3, 3, 6, 6, 6, 6],

[0, 0, 0, 3, 3, 3, 6, 7, 7, 7],

[0, 0, 0, 3, 3, 3, 6, 7, 7, 9],

[0, 1, 1, 3, 4, 4, 6, 7, 8, 9]]

LIS问题

longest increasing subsequence问题,

# 讲DP基本都会讲到的一个问题LIS:longest increasing subsequence

# http://www.deeplearn.me/216.html

lis = [2 ,1, 5, 3, 6 ,4 ,8 ,9, 7]

d = [1]*len(lis)

res = 1

for i in range(len(lis)):

for j in range(i):

if lis[j] <= lis[i] and d[i] < d[j]+1:

d[i] = d[j]+1

if d[j] > res:

res = d[j]

print res

LCS问题

# 根据图解教程写的伪代码,其实最后评论里面的代码就是我添加上去的

s1 = [1,3,4,5,6,7,7,8]

s2 = [3,5,7,4,8,6,7,8,2]

d = [[0]*(len(s2)+1) for i in range(len(s1)+1) ]

for i in range(1,len(s1)+1):

for j in range(1,len(s2)+1):

if s1[i-1] == s2[j-1]:

d[i][j] = d[i-1][j-1]+1

else:

d[i][j] = max(d[i-1][j],d[i][j-1])

print "max LCS number:",d[-1][-1]

给定一个有n个正整数的数组A和一个整数sum

​ 给定一个有n个正整数的数组A和一个整数sum,求选择数组A中部分数字和为sum的方案数。

当两种选取方案有一个数字的下标不一样,我们就认为是不同的组成方案。

输入描述:

输入为两行:

第一行为两个正整数n(1 ≤ n ≤ 1000),sum(1 ≤ sum ≤ 1000)

第二行为n个正整数A[i](32位整数),以空格隔开。

输出描述:

输出所求的方案数

示例1

输入

5 15

5 5 10 2 3

输出

4

#动态规划算法。dp[i][j]代表用前i个数字凑到j最多有多少种方案。

#dp[i][j]=dp[i-1][j]; //不用第i个数字能凑到j的最多情况

#dp[i][j]+=dp[i-1][j-value[i]];用了i时,只需要看原来凑到j-value[i]的最多情况即可。并累加

num_ = 5

sum_ = 10

line = [5 ,5 ,10 ,2 ,3]

optp = [[1]+[0]*sum_ for i in range(num_+1)] # 第一列为1的原因是和为0的时候只有一种取法,就是什么都不取

for i in range(1,num_+1):

for j in range(1,sum_+1):

if j - line[i-1] >=0:

optp[i][j] = optp[i-1][j] + optp[i-1][j-line[i-1]]

else:

optp[i][j] = optp[i-1][j]

print optp

0 1 2 3 4 5 6 7 8 9 10

0 [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],

5 [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],

5 [1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1],

10 [1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2],

2 [1, 0, 1, 0, 0, 2, 0, 2, 0, 0, 2],

3 [1, 0, 1, 1, 0, 3, 0, 2, 2, 0, 4]]

# 转化为背包问题后,开始推,(注意第一行和第一列是预至位)比如说第一个数字是5,那么从构成和为1,怎么取?当然没得取,直到构成和为5的时候,开始执行,如果用这个5,那么还剩下5-5=0的和,0的和取法只有1,而如果不用5,取法只有0,所以为1,之后重复推,再说第二个5,直到和为5之前,都是0取法,到了5之后,两种取法,一种是要不要这个新的5,如果要这个新的5,那么剩下的和即5-5=0,一种取法,如果这个新的5不取,那以前能取到和为5就上次循环中的一种取法,所以合起来两种取法

一个数组有 N 个元素,求连续子数组的最大和

一个数组有 N 个元素,求连续子数组的最大和。 例如:[-1,2,1],和最大的连续子数组为[2,1],其和为 3

输入描述:

输入为两行。

第一行一个整数n(1 <= n <= 100000),表示一共有n个元素

第二行为n个数,即每个元素,每个整数都在32位int范围内。以空格分隔。

输出描述:

所有连续子数组中和最大的值。

示例1

输入

3

-1 2 1

输出

3

# 采用动态规划的方法

# 设dp[i]表示以第 i个元素为结尾的连续子数组的最大和,则递推方程式为 dp[i]=max{dp[i-1]+a[i], a[i]};

num = raw_input("")

line = raw_input("")

line = map(lambda x:int(x),line.split(" "))

num = int(num)

d =[0]*(num-1)

d.insert(0,line[0])

for i in range(1,num):

d[i] = max(d[i-1]+line[i],line[i])

print max(d)

X*Y的网格迷宫

有一个X*Y的网格,小团要在此网格上从左上角到右下角,只能走格点且只能向右或向下走。请设计一个算法,计算小团有多少种走法。给定两个正整数int x,int y,请返回小团的走法数目。

输入描述:

输入包括一行,逗号隔开的两个正整数x和y,取值范围[1,10]。

输出描述:

输出包括一行,为走法的数目。

示例1

输入

3 2

输出

10

# 动态规划,使用递推方程d[i][j] = d[i-1][j] + d[i][j-1]

# 因为可能从两个方向走到同一个点,所以从上到下为一种走法,从左到右是另一种走法

# 注意题目给的是x*y方格,所以是(x+1)*(y+1)个点

line = map(int, raw_input("").split(" "))

x = line[0]

y = line[1]

d = [[0]*(y+2) for i in range(x+2)]

for i in range(1,x+2):

for j in range(1,y+2):

if i==j and i==1:

d[i][j] = 1

else:

d[i][j] = d[i-1][j] + d[i][j-1]

print d[-1][-1]

暗黑字符串

一个只包含'A'、'B'和'C'的字符串,如果存在某一段长度为3的连续子串中恰好'A'、'B'和'C'各有一个,那么这个字符串就是纯净的,否则这个字符串就是暗黑的。例如:

BAACAACCBAAA 连续子串"CBA"中包含了'A','B','C'各一个,所以是纯净的字符串

AABBCCAABB 不存在一个长度为3的连续子串包含'A','B','C',所以是暗黑的字符串

你的任务就是计算出长度为n的字符串(只包含'A'、'B'和'C'),有多少个是暗黑的字符串。

输入描述:

输入一个整数n,表示字符串长度(1 ≤ n ≤ 30)

输出描述:

输出一个整数表示有多少个暗黑字符串

示例1

输入

3

输出

21

思路解析

#方式二,这么low的方式是我根据上面的解析写的。递归所以速度慢

num = int(raw_input(""))

def dark(num):

if num == 1:

return 3

elif num==2:

return 9

else:

return 2*dark(num-1) + dark(num-2)

print dark(num)

# 方式一:别人家的代码

n = int(raw_input())

dp = [0]*31

dp[0] = 3

dp[1] = 9

for i in xrange(2, n):

dp[i] = 2*dp[i-1]+dp[i-2]

print dp[n-1]

最后

纸上得来终觉浅,这句话放在什么时候都一样,自己觉得动态规划比较了解了,其实了解个屁,需要重新打打基础!以后再过来更新理解。

致谢

dpresult在python中什么意思_动态规划(DP)的整理-Python描述相关推荐

  1. 什么是python中子类父类_零基础入门:python中子类继承父类的__init__方法实例

    前言: 今天为大家带来的内容是零基础入门:python中子类继承父类的__init__方法实例!具有不错的参考意义,希望在此能够帮助到各位!(喜欢的话记得点赞转发关注不迷路哦) 使用Python写过面 ...

  2. python中可选参数_带可选参数的Python函数

    我试图改进Python中的函数.它需要几个参数,其中一些可能会丢失. def some_function (self, a, b, c, d = None, e = None, f = None, g ...

  3. python中datetime库_一天掌握一个Python库--datetime库

    #一天掌握一个Python库--datetime库 **datatime** 模块题共用一些处理日期,时间和时间间隔的函数.这个模块使用面向对象的交互取代了**time**模块中整形/元组类型的时间函 ...

  4. python中的映射_一文读懂Python中的映射

    python中的反射功能是由以下四个内置函数提供:hasattr.getattr.setattr.delattr,改四个函数分别用于对对象内部执行:检查是否含有某成员.获取成员.设置成员.删除成员. ...

  5. python中factor函数_左手用R右手Python系列——因子变量与分类重编码

    原标题:左手用R右手Python系列--因子变量与分类重编码 欢迎关注天善智能 hellobi.com,我们是专注于商业智能BI,大数据,数据分析领域的垂直社区,学习.问答.求职,一站式搞定! 对商业 ...

  6. python中write函数_第9.7节 Python使用write函数写入文件内容

    一.语法 write(data) data为要写入的数据,可以为字符串str类型,也可以是bytes类型. 返回值为实际写入的数据数,在写入数据为str类型时,该数据为实际写入的UNIOCODE字符数 ...

  7. 怎么在python中输入矩阵_如何使用NumPy在Python中实现矩阵?

    矩阵被用作数学工具,在现实世界中有多种用途.在本文中,我们将按照以下顺序讨论Python中关于使用著名的NumPy库的矩阵的所有内容:什么是NumPy以及何时使用它?在NumPy 矩阵被用作数学工具, ...

  8. python中timeit函数_一日一技:Python中的timeit方法

    timeit()方法 python中的timeit()方法, 它用于获取代码的执行时间.该库将代码语句运行一百万次,并提供从集合中花费的最短时间.这是一种有用的方法,有助于检查代码的性能. 语法如下: ...

  9. python中strip函数_让你整明白python strip函数妙用

    以下为作者学习python时所做的学习笔记,用浅显的语言展示给大家,希望大家多多提交自己的意见.有问题的朋友也可以留言,看到会回复大家. 声明:s为字符串,str为要删除的字符序列 s.strip(s ...

最新文章

  1. Linux系统火焰图
  2. python压缩与解压缩
  3. 打开AD组策略编辑器提示“strings区段项目太长被截断”的解决
  4. python语言需要变量生明吗_python需要定义变量吗
  5. SAP CRM SPRO Customizing 里 Text Object 属性 continue 的含义
  6. php背景图添加字,怎样给视频后面加背景图 视频加背景图片并添加一行广告文字...
  7. TensorFlow第六步: 继续挖坑 用tf重写BP并增加SGD
  8. 测试开发:聊一聊自动化测试框架,值得收藏!
  9. 【简单思考】noip模拟赛 NTR酋长
  10. 63. 无阻塞加载脚本
  11. 驰骋工作流引擎表单设计器--表单装载前数据填充
  12. 西门子s7-1200博图v16灌装机PLC程序+西门子KTP1200触摸屏程序,设备上已使用的程序
  13. 用PS合并RGB图片和只有透明通道的图片
  14. 谷歌 发布android 8,2017谷歌大会正式发布Android 8.0!
  15. 一元多项式计算器 (c语言数据结构实验)
  16. class SequenceFileOutputFormat takes type parameters
  17. 教你做表格(史上最全)
  18. scons 手册_SCons笔记(详细版)
  19. git使用及上传代码到github
  20. 信用风险建模 in Python 系列 7 - ASRF 模型

热门文章

  1. IoUattack复现
  2. 2022-1-15 计算机组成原理 —— 如何提升性能
  3. 2021-11-14剑指OfferII014.字符串中的变位词
  4. 关于达梦数据库bak格式文件还原操作
  5. Android常用控件之ExpandableList的使用
  6. 2021-06-27剑指offer 14-1.剪绳子
  7. 【软件配置技能001】CAJ文件不存在或不能正常访问的解决办法
  8. 一分钟了解直播电商与微商的区别
  9. 使用camtasia studio制作视频心得
  10. item_search - 按关键字搜索1688商品