青训营 动态规划入门以及一般解法 | 豆包MarsCode AI刷题
案例引入
在今日进行AI刷题的练习时,遇到一道有趣的题目。
在看到题目详细描述后,考虑这题应该使用递归来写,进一步思考后决定使用动态规划来解决。因为这道题只需要判断是否到达,因此只涉及最简单的动态规划调用。
动态规划一般解法
动态规划(Dynamic Programming, DP)是一种常用的算法设计技术,适用于解决具有重叠子问题和最优子结构性质的问题。以下是动态规划做题的一般套路:
-
定义状态:
- 状态:通常表示问题的某个子问题的解。状态的定义需要能够覆盖所有可能的子问题。
- 状态变量:用于表示状态的变量。例如,
dp[i]可能表示前i个元素的最优解。
-
确定状态转移方程:
- 状态转移方程:描述如何从一个状态转移到另一个状态。这是动态规划的核心,通常通过递推关系来表示。
- 例如,
dp[i] = max(dp[i-1], dp[i-2] + array[i])表示当前状态dp[i]可以通过前一个状态dp[i-1]或前两个状态dp[i-2]加上当前元素array[i]得到。
-
初始化与结束标准:
- 初始状态:确定问题的开始状态,即为最简单的情况。
- 结束边界:确定递归调用结束边界,例如在到达路径终点时停止递归并返回。
-
返回参数:
- 根据状态定义来确定函数调用需要返回的参数,从而来返回最终状态的值。
题目详解
-
回到这道题目中,这道题小R设计的机器人每次移动只能消耗当前位置的能量,因此每个状态中需要考虑的条件只和当前位置有关。从而在定义参数时只考虑当前索引i。同时因为小R只考虑能否到达,所以将返回参数设立为bool值类型来判断是否到达。使用python解答:
def dp(i:int)->bool //i为当前到达位置,返回为是否能到达终点 -
考虑结束条件:
在实际的动态规划题目中,应当在函数开头写出判定结束条件。本题中,小R仅考虑是否到达。因此当当前索引i大于目的位置时候便返回成功;同时如果当前索引在到达n之前能量为0(即无法继续前进)返回失败。
if i>=n-1: return True if array[i]==0: return False -
状态转移:
本题中,进入下一个状态即前进需要消耗当前位置的能量,因此需要根据当前位置的能量来判断前进距离。同时因为本题只需要判断是否能够到达,而不考虑到达路径的多少,因此进行剪枝操作。只要有一条道路能够前往终点就直接停止递归。
for j in range(array[i]): ok=dp(i+j+1)//前进不超过aaray[i]的步数 if ok: return True//只要成功就不再往后考虑 -
初始化:
本题只需要考虑从起点出发,因此直接调用dp(0)即可,同时根据返回的bool值判断是否能够到达终点
res=dp(0)//从起点出发 if res: return "TRUE" else: return "FALSE"//根据res值返回结果
本题完整代码:
def solution(n, array):
# Edit your code here
def dp(i:int)->bool:
if i>=n-1:
return True
if array[i]==0:
return False
for j in range(array[i]):
ok=dp(i+j+1)
if ok:
return True
return False
res=dp(0)
if res:
return "TRUE"
else:
return "FALSE"
if __name__ == "__main__":
# Add your test cases here
print(solution(5, [2, 3, 1, 1, 4]) == "TRUE")
print(solution(5, [3, 2, 1, 0, 4]) == "FALSE")
提交结果:
总结与归纳
本题虽然在AI刷题中标识难度为中等,但实际为最简单的动态规划递归调用,只需要考虑当前位置索引,同时只需要考虑是否能到达目的,因此也最为适合动态规划入门。在实际题目中,从状态的设立、转移、结束方向进行思考,从而确定函数中需要的参数与返回值便能确定大致思路。同时为了优化时间复杂度,可以考虑进行记忆化搜索以及剪枝操作。