动态规划学习笔记 // /经典问题/状态表示/状态转移方程

  • 因为网络原因,图片以及部分经典问题没有上传成功,详见PDF。

  • 首先要做的是——写出状态表示和状态转移方程,有可能会引入k,即对于子问题的界定。——状态值dp[][]要和题目所求内容的要求符合!

  • 子问题——很重要,把问题转化为多个阶段的子问题。

  • 多阶段决策过程,每一阶段决策都是子问题——一步边长+上一个子问题结果

  • 最优子结构性质:全局最优的子状态一定是子问题的最优。

  • 一个反例:mod 10——不满足最优子结构性质,所以不能使用动态规划。

  • 动态规划算法设计:

    • 目标函数,约束条件

    • 划分子问题,确定边界(k)——将问题转化为多步求解

    • 优化函数值,子问题最优值——判断是否满足优化原则

    • 递推方程,初值,

    • 自底向上,备忘录( 表格)

    • 考虑是否设立 标记函数

    • 递推方程或备忘录 估计时间复杂度

    • 最短路径

      • https://blog.csdn.net/u010402786/article/details/50856056

      • https://blog.csdn.net/hpulw/article/details/48053583

      • 矩阵链相乘

      • ——总次数最小

      • A(i,j) B(j,k)=C,C是i行k列,所以计算每个元素需要j次乘法,j-1次加法,总计乘法次数为i*j*k

      • 动态规划的递归实现:——效率不高,原因是:同一子问题对此重复计算!!!——怎么改进——空间换时间——结果记录下来

        • 1.划分子问题,

        • 2.子问题依赖关系:假设最优划分在K位置, (界定边界)

        • 3.递推方程:

        • 4.初值:对于最小子问题的最优值

        • 5.最优子结构性质(证明可以使用动态规划)

        • 时间复杂度:数学归纳法

        • 动态规划的迭代实现:——每个子问题只计算一次

        • 从最小的子问题开始计算,

        • 注意计算顺序,

        • 存储结构——备忘录

        • 解的追踪——设计标记函数,标记每一步的决策

        • 动态规划的时间复杂度——备忘录各项计算量之和(+追踪解工作量)

        • 动态规划的经典问题——(关于两个字符串)

          •    最长公共子序列。

          •    交错字符。

          •    字符串编辑距离。(应用:DNA分析,拼写纠错,实体共指,字符串核函数)

          • ——状态dp[i][j]表示s1[0]到s1[i]和s2[0]到s2[j]的一些关系。

        •  

        •  交错字符

          • 描述状态表示和状态转移方程:

            • 状态表示:dp[i][j] 代表是A的前i个字符与C中匹配,B中前j个字符与C中匹配.

            • 状态转移方程:f[i][j] = (f[i-1][j] && A[i-1] == C[i+j-1]) || (f[i][j-1] && B[j-1] == C[i+j-1])

            • 初始化:f[0][i] = B.prefix(i) == C.prefix(i); f[i][0] = A.prefix(i) == C.prefix(i);

        • 字符串编辑距离

          •     ——插入,删除,替换

          • 譬如,"kitten" 和 "sitting" 这两个单词,由 "kitten" 转换为 "sitting" 需要的最少单字符编辑操作有:

            • 1.kitten → sitten (substitution of "s" for "k")

            • 2.sitten → sittin (substitution of "i" for "e")

            • 3.sittin → sitting (insertion of "g" at the end)

            • 因此,"kitten" 和 "sitting" 这两个单词之间的编辑距离为 3 。

        •  

                  

        • 最大连续子序列乘积

          • 访问到每个点的时候,以该点为子序列的末尾的乘积,要么是该点本身,要么是该点乘以以前一点为末尾的序列,注意乘积负负得正,故需要记录前面的最大最小值。

          • dp1[i]:以第i个数结尾的连续子序列最大乘积

          • dp2[i]:以第i个数结尾的连续子序列最小乘积

          • 转移方程:

            • dp1[i]=max(data[i],dp1[i-1]*data[i],dp2[i-1]*data[i]);

            • dp2[i]=min(data[i],dp1[i-1]*data[i],dp2[i-1]*data[i]);

        • 背包问题

          • 子问题界定:

            • 由参数 k 和 y 界定         

            •  k:考虑对物品1, 2, ... , k 的选择       

            •  y:背包总重量不超过  y    

          • 原始输入:

            • k = n,  y = b

          • 子问题计算顺序:         

            • k = 1, 2, ... , n         

              • 对于给定的 k,y = 1, 2, ... , b    

        •  

           

        • 硬币找零

        • 假设有几种硬币,如1、3、5,并且数量无限。请找出能够组成某个数目的找零所使用最少的硬币数。
          • 这是一道经典的动态规划方法,我们可以维护一个一维动态数组dp,其中dp[i]表示钱数为i时的最小硬币数的找零,递推式为:dp[i] = min(dp[i], dp[i - coins[j]] + 1);
          • 其中coins[j]为第j个硬币,而i - coins[j]为钱数i减去其中一个硬币的值,剩余的钱数在dp数组中找到值,然后加1和当前dp数组中的值做比较,取较小的那个更新dp数组。
        • 假设有几种硬币,每个值代表一张钱的面值,再给定一个整数代表要找的钱数,求换钱有多少种方法。
          • 一个硬币一个硬币的增加,每增加一个硬币,都从1遍历到amount,对于遍历到的当前钱数j,组成方法就是不加上当前硬币的频发dp[i-1][j],还要加上,去掉当前硬币值的钱数的组成方法,当然钱数j要大于当前硬币值,
          • 那么我们的递推公式为:dp[i][j] = dp[i - 1][j] + (j >= coins[i - 1] ? dp[i][j - coins[i - 1]] : 0)
          • 0.....i的货币,组成j元钱
          • 注意我们要初始化每行的第一个位置为1。
        • 一个矩形区域被划分为N*M个小矩形格子,在格子(i,j)中有A[i][j]个苹果。现在从左上角的格子(1,1)出发,要求每次只能向右走一步或向下走一步,最后到达(N,M),每经过一个格子就把其中的苹果全部拿走。请找出能拿到最多苹果数的路线。
          • 分析:这道题中,当前位置(i,j)是状态,
          • 用M[i][j]来表示到达状态(i,j)所能得到的最多苹果数,
          • 那么M[i][j] = max(M[i-1][j],M[i][j-1]) + A[i][j] 。特殊情况是M[1][1]=A[1][1],当i=1且j!=1时,M[i][j] = M[i][j-1] + A[i][j];当i!=1且j=1时M[i][j] = M[i-1][j] + A[i][j]。
        •  

           

你可能感兴趣的:(动态规划学习笔记 // /经典问题/状态表示/状态转移方程)