前言
以下内容我是和左神学的,更多专业算法知识请向左神学习!
动态规划初识
动态规划算法:通过构建一系列子问题,求解部分子问题,然后通过子问题的依赖关系,求解出所有子问题,一般来说最后的那个子问题就是最终需要的答案。
公式描述就是:
构建子问题
p
1
,
p
2
,
p
3
,
.
.
.
p
n
p_{1},p_{2},p_{3},...p_{n}
p1,p2,p3,...pn
求解部分子问题
p
1
p_{1}
p1
状态转移
p
i
=
f
(
p
i
−
1
)
p_{i}=f(p_{i-1})
pi=f(pi−1)
得到答案
r
e
s
u
l
t
=
p
n
result=p_{n}
result=pn
动态规划算法的关键就是,如何构建子问题?如何找到状态转移方程?
模板思路
暴力递归 -> 记忆化搜索 -> 动态规划
暴力递归大家应该都懂,使用暴力尝试的方法求解问题,主要是用计算机的计算思维、搜索思维解决问题。
记忆化搜索,在暴力递归的基础上加了小改进,当我们已经计算过 function(x,y)后,下次不用再计算了,直接返回结果,具体的实现方法可通过简单打表实现,计算出值就放入表,下次再需要这个值直接查表。
动态规划,通过前两部分的尝试,我们已经可以看出来动态转移方程是什么样的,动态规划是一种高度精巧的算法模型,往往会有更快的速度,更少的空间复杂度(动态规划状态表有时可优化)。
题目示例
题目描述
- 地下城游戏 (hard)
一些恶魔抓住了公主(P)并将她关在了地下城的右下角。地下城是由 M x N 个房间组成的二维网格。我们英勇的骑士(K)最初被安置在左上角的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。
骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。
有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。
为了尽快到达公主,骑士决定每次只向右或向下移动一步。
编写一个函数来计算确保骑士能够拯救到公主所需的最低初始健康点数。
例如,考虑到如下布局的地下城,如果骑士遵循最佳路径 右 -> 右 -> 下 -> 下,则骑士的初始健康点数至少为 7。
-2(K) | -3 | 3 |
---|---|---|
-5 | -10 | 1 |
10 | 30 | -5 (P) |
说明:
骑士的健康点数没有上限。
任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。
解析
#记忆化搜索版本
def calculateMinimumHP(self, dungeon: List[List[int]]) -> int:
lengthx=len(dungeon)
lengthy=len(dungeon[0])
@lru_cache(None) #函数修饰器,缓存,偷个懒哈哈
def get_min_sum(x,y):
if x==len(dungeon)-1 and y==lengthy-1:
return dungeon[x][y]
if x==len(dungeon)-1:
return min(dungeon[x][y],dungeon[x][y]+ get_min_sum(x,y+1))
if y==lengthy-1:
return min(dungeon[x][y],dungeon[x][y]+ get_min_sum(x+1,y))
return min(dungeon[x][y],dungeon[x][y]+max( get_min_sum(x+1,y),get_min_sum(x,y+1) ) )
min_sum=get_min_sum(0,0)
if min_sum>0:
return 1
else:
return -min_sum+1
#动态规划版本
def calculateMinimumHP(self, dungeon: List[List[int]]) -> int:
lengthx=len(dungeon)
lengthy=len(dungeon[0])
for y in range(lengthy-2,-1,-1):
dungeon[lengthx-1][y]=min( dungeon[lengthx-1][y] , dungeon[lengthx-1][y]+dungeon[lengthx-1][y+1] )
for x in range(lengthx-2,-1,-1):
dungeon[x][lengthy-1]=min( dungeon[x][lengthy-1] , dungeon[x][lengthy-1]+dungeon[x+1][lengthy-1] )
for x in range(lengthx-2,-1,-1):
for y in range(lengthy-2,-1,-1):
dungeon[x][y]=min(dungeon[x][y],dungeon[x][y]+max(dungeon[x+1][y],dungeon[x][y+1]))
if dungeon[0][0]>0:
return 1
else:
return -dungeon[0][0]+1