青训营 动态规划入门以及一般解法 | 豆包MarsCode AI刷题

117 阅读4分钟

青训营 动态规划入门以及一般解法 | 豆包MarsCode AI刷题

案例引入

在今日进行AI刷题的练习时,遇到一道有趣的题目。

image-20241109145911252.png

在看到题目详细描述后,考虑这题应该使用递归来写,进一步思考后决定使用动态规划来解决。因为这道题只需要判断是否到达,因此只涉及最简单的动态规划调用。

动态规划一般解法

动态规划(Dynamic Programming, DP)是一种常用的算法设计技术,适用于解决具有重叠子问题和最优子结构性质的问题。以下是动态规划做题的一般套路:

  1. 定义状态:

    • 状态:通常表示问题的某个子问题的解。状态的定义需要能够覆盖所有可能的子问题。
    • 状态变量:用于表示状态的变量。例如,dp[i] 可能表示前 i 个元素的最优解。
  2. 确定状态转移方程:

    • 状态转移方程:描述如何从一个状态转移到另一个状态。这是动态规划的核心,通常通过递推关系来表示。
    • 例如,dp[i] = max(dp[i-1], dp[i-2] + array[i]) 表示当前状态 dp[i] 可以通过前一个状态 dp[i-1] 或前两个状态 dp[i-2] 加上当前元素 array[i] 得到。
  3. 初始化与结束标准:

    • 初始状态:确定问题的开始状态,即为最简单的情况。
    • 结束边界:确定递归调用结束边界,例如在到达路径终点时停止递归并返回。
  4. 返回参数:

    • 根据状态定义来确定函数调用需要返回的参数,从而来返回最终状态的值。

题目详解

  1. 回到这道题目中,这道题小R设计的机器人每次移动只能消耗当前位置的能量,因此每个状态中需要考虑的条件只和当前位置有关。从而在定义参数时只考虑当前索引i。同时因为小R只考虑能否到达,所以将返回参数设立为bool值类型来判断是否到达。使用python解答:

     def dp(i:int)->bool
     //i为当前到达位置,返回为是否能到达终点
    
  2. 考虑结束条件:

    在实际的动态规划题目中,应当在函数开头写出判定结束条件。本题中,小R仅考虑是否到达。因此当当前索引i大于目的位置时候便返回成功;同时如果当前索引在到达n之前能量为0(即无法继续前进)返回失败。

     if i>=n-1:
         return True
     if array[i]==0:
         return False
    
  3. 状态转移:

    本题中,进入下一个状态即前进需要消耗当前位置的能量,因此需要根据当前位置的能量来判断前进距离。同时因为本题只需要判断是否能够到达,而不考虑到达路径的多少,因此进行剪枝操作。只要有一条道路能够前往终点就直接停止递归。

     for j in range(array[i]):
                ok=dp(i+j+1)//前进不超过aaray[i]的步数
                if ok:
                    return True//只要成功就不再往后考虑
    
  4. 初始化:

    本题只需要考虑从起点出发,因此直接调用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")

提交结果:

image-20241109152500024.png

总结与归纳

本题虽然在AI刷题中标识难度为中等,但实际为最简单的动态规划递归调用,只需要考虑当前位置索引,同时只需要考虑是否能到达目的,因此也最为适合动态规划入门。在实际题目中,从状态的设立、转移、结束方向进行思考,从而确定函数中需要的参数与返回值便能确定大致思路。同时为了优化时间复杂度,可以考虑进行记忆化搜索以及剪枝操作。