动态规划Dynamic programming笔记

  • 笔记用途
  • 介绍及解题思路
    • 例题:CoinChange
  • 题目类型分类
    • 坐标型动态规划
      • Unique Paths
      • Unique Paths II
      • 最小路径和
      • 轰炸敌人(Bomb Enemy)
    • 序列型动态规划
      • Paint House
      • 涂房子2(Paint House2)
      • 最长连续单调子序列
      • Decode Ways
      • 比特位技术(Counting Bits)
      • 打家劫舍(House Robber)
      • 打家劫舍 II(House Robber II)
      • 买卖股票的最佳时机(Best Time To Buy And Sell Stock)
      • 买卖股票的最佳时机 III(Best Time To Buy And Sell Stock III)建议回看视频
      • 买卖股票的最佳时机 IV(Best Time To Buy And Sell Stock IV)5阶段拓展到2k+1
      • 最长上升子序列(Longest Increasing Subsequence)
      • 俄罗斯套娃信封问题(Russian Doll Envelopes)
    • 双序列型动态规划(特殊,属于序列型的拓展)
      • 最长公共子序列(Longest Common Subsequence)
      • 交叉字符串(Interleaving String)
      • 编辑距离(Interleaving String)
      • 不同的子序列(Distinct Subsequences)
      • 正则表达式(Regular Expression Matching)
    • 划分型动态规划
      • 完全平方数(Perfect Squares)
      • 分割回文串II(Palindrome Partitioning II)
      • 书籍复印(Palindrome Partitioning II)
    • 区间型动态规划
      • 最长的回文序列(Longest Palindromic Subsequence)
      • 扰乱字符串(Longest Palindromic Subsequence)
      • 戳气球(Burst Balloons)
    • 背包型动态规划
      • 背包问题(Backpack)
      • 背包问题V(BackpackV)
      • 背包问题II(BackpackII)
      • 背包问题III(BackpackIII)在二的基础上不限个数
    • 博弈型动态规划
      • Jump game
      • 硬币排成线(Coins in A Line)
      • 硬币排成线III(Coins in A LineIII)

笔记用途

写这篇文章的目的主要是想在接下来的解题过程中能够通过这篇文章快速的回想起当初解题的思路,如果没有看过视频建议先去观看一下动态规划专题讲解,视频放在了代码中,相关题目也可以在leetcode中找到,如果对于你有帮助,给我点个赞吧!
链接:https://pan.baidu.com/s/1DAnUbgWvdS5o8_WeAFzdlA
提取码:7b3d
复制这段内容后打开百度网盘手机App,操作更方便哦

介绍及解题思路

动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。能采用动态规划求解的问题的一般要具有3个性质:
(1) 最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。
(2) 无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。
(3) 有重叠子问题即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)
动态规划解题思路(参考B站九章算法):
(1) 确定状态:分为最后一步以及子问题
(2) 写状态转移方程
(3) 规定初始条件以及边界情况
(4) 计算顺序(消除冗余优化空间)

例题:CoinChange

题目链接: link.
假设你有三种硬币,分别面值2元,5元和7元,每种硬币都有足够多 , 买一本书需要27元 ,如何用最少的硬币组合正好付清,且不需要对方找钱。
(1) 确定状态:状态在动态规划中的作用属于定海神针 。简单的说,解动态规划的时候需要开一个数组,数组的每个元素f[i]或者f[i][j]代表什么– 类似于解数学题中,X,Y,Z代表什么。
最后一步是最优策略肯定是K枚硬币a1, a2,…, aK 面值加起来是27所以一定有一枚最后的硬币: aK • 除掉这枚硬币,前面硬币的面值加起来是27- aK
子问题最少用多少枚硬币可以拼出27- aK。
(2) 写状态转移方程
最后那枚硬币aK只可能是2,5或者7 ;如果aK是2,f(27)应该是f(27-2) + 1 (加上最后这一枚硬币2);如果aK是5,f(27)应该是f(27-5) + 1 (加上最后这一枚硬币5) ;如果aK是7,f(27)应该是f(27-7) + 1 (加上最后这一枚硬币7)。

所以状态转移方程为:

(3) 规定初始条件以及边界情况
初始条件:f[0] = 0
边界情况:X-2, X-5 或者X-7小于0时无法拼出定义为正无穷。
(4) 计算顺序(消除冗余优化空间)
初始条件:f[0] = 0
• 然后计算f[1], f[2], …, f[27]
• 当我们计算到f[X]时,f[X-2], f[X-5], f[X-7]都已经得到结果了

    public int coinChange(int[] coins, int amount){int [] dp = new int[amount+1];int n = coins.length;//初始化dp[0] = 0;//状态转移方程for (int i = 1;i <= amount;i++){dp[i] = Integer.MAX_VALUE;for (int j = 0;j < n;j++){//考虑边界情况if (i>=coins[j] && dp[i-coins[j]] != Integer.MAX_VALUE ){dp[i] = Math.min(dp[i],dp[i-coins[j]]+1);}}}//表示无法拼出返回-1if (dp[amount]==Integer.MAX_VALUE){return -1;}return dp[amount];}

题目类型分类

动态规划类题目主要分类为:
– 坐标型动态规划 (20%)重要
– 序列型动态规划 (20%)重要
– 划分型动态规划 (20%)重要
– 区间型动态规划 (15%)重要
– 背包型动态规划 (10%)重要
– 拓扑型动态规划 (5%)
– 博弈型动态规划 (5%)
– 综合性动态规划 (5%)

坐标型动态规划

• 最简单的动态规划类型
• 给定一个序列或网格
• 需要找到序列中某个/些子序列或网格中的某条路径 – 某种性质最大/最小 – 计数 – 存在性
• 动态规划方程f[i]中的下标i表示以ai为结尾的满足条件的子序列的性质,f[i][j] 中的下标i, j表示以格子(i, j)为结尾的满足条件的路径的性质
– 最大值/最小值 – 个数
– 是否存在
• 坐标型动态规划的初始条件f[0]就是指以a0为结尾的子序列的性质

Unique Paths

题目链接: link.
有一个机器人的位于一个 m × n 个网格左上角。机器人每一时刻只能向下或者向右移动一步。机器人试图达到网格的右下角。问有多少条不同的路径?
(1) 确定状态:分为最后一步以及子问题
最后一步:无论机器人用何种方式到达右下角,总有最后挪动的一步:向右 或者 向下右下角坐标设为(m-1, n-1),那么前一步机器人一定是在(m-2, n-1)或者(m-1, n-2)
子问题为:机器人有多少种方式从左上角走到(m-2, n-1)和(m-1, n-2)
(2) 写状态转移方程

(3) 规定初始条件以及边界情况
初始条件:f[0][0] = 1,因为机器人只有一种方式到左上角(什么都不做)
边界情况:i = 0 或 j = 0,则前一步只能有一个方向过来及f[i][j]=1(i0或者 j0)
(4) 计算顺序(消除冗余优化空间)
• f[0][0] = 1
• 计算第0行:f[0][0], f[0][1], …, f[0][n-1]
• 计算第1行:f[1][0], f[1][1], …, f[1][n-1]
• …• 计算第m-1行:f[m-1][0], f[m-1][1], …, f[m-1][n-1]

Unique Paths II

题目链接: link.
• 给定m行n列的网格,有一个机器人从左上角(0,0)出发,每一步可以向下 或者向右走一步
• 网格中有些地方有障碍,机器人不能通过障碍格
• 问有多少种不同的方式走到右下角

(1) 确定状态:分为最后一步以及子问题
最后一步:无论机器人用何种方式到达右下角,总有最后挪动的一步:向右 或者 向下右下角坐标设为(m-1, n-1),那么前一步机器人一定是在(m-2, n-1)或者(m-1, n-2)

子问题为:机器人有多少种方式从左上角走到(m-2, n-1)和(m-1, n-2)

(2) 写状态转移方程

(3) 规定初始条件以及边界情况
初始条件:f[0][0] = 1,因为机器人只有一种方式到左上角(什么都不做)
边界情况:

(4) 计算顺序(消除冗余优化空间)
• f[0][0] = 1
• 计算第0行:f[0][0], f[0][1], …, f[0][n-1]
• 计算第1行:f[1][0], f[1][1], …, f[1][n-1]
• …• 计算第m-1行:f[m-1][0], f[m-1][1], …, f[m-1][n-1]

最小路径和

题目链接: link.
• 题意:
• 给定m行n列的网格,每个格子(i, j)里都一个非负数A[i][j]
• 求一个从左上角(0, 0)到右下角的路径,每一步只能向下或者向右走一步
• 使得路径上的格子里的数字之和最小
• 输出最小数字和

(1) 确定状态:分为最后一步以及子问题
最后一步:无论机器人用何种方式到达右下角,总有最后挪动的一步:向右 或者 向下右下角坐标设为(m-1, n-1),那么前一步机器人一定是在(m-2, n-1)或者(m-1, n-2)路径和最小

子问题为:机器人有从左上角走到(m-2, n-1)和(m-1, n-2)的路径和最小

(2) 写状态转移方程

(3) 规定初始条件以及边界情况
初始条件:初始条件:f[0][0] = A[0][0])
边界情况: i= 0 或 j = 0,则前一步只能有一个方向过来

(4) 计算顺序(消除冗余优化空间)
• f[0][0] = 1
• 计算第0行:f[0][0], f[0][1], …, f[0][n-1]
• 计算第1行:f[1][0], f[1][1], …, f[1][n-1]
• …
• 计算第m-1行:f[m-1][0], f[m-1][1], …, f[m-1][n-1]
• f[i][j] = f[i-1][j] + f[i][j-1] • 时间复杂度(计算步数):O(MN),空间复杂度(数组大小):O(MN)

轰炸敌人(Bomb Enemy)

题目链接: link.
• 题意: • 有一个M*N的网格,每个格子可能是空的,可能有一个敌人,可能有一 堵墙 • 只能在某个空格子里放一个炸弹,炸弹会炸死所有同行同列的敌人,但 是不能穿透墙 • 最多能炸死几个敌人

(1) 确定状态:分为最后一步以及子问题
最后一步:在(i, j)格放一个炸弹,它向上能炸死的敌人数是:
– (i, j)格为空地:(i-1, j)格向上能炸死的敌人数
– (i, j)格为敌人: (i-1, j)格向上能炸死的敌人数+1
– (i, j)格为墙:0

子问题为:需要知道(i-1, j)格放一个炸弹向上能炸死的敌人数 • 原来要求 (i, j)格放一个炸弹向上能炸死的敌人数

(2) 写状态转移方程
状态: Up[i][j]表示(i, j)格放一个炸弹向上能炸死的敌人数

(3) 规定初始条件以及边界情况
初始条件:第0行的Up值和格子内容相关
– Up[0][j] = 0, 如果(0,j)格不是敌人
– Up[0][j] = 1, 如果(0,j)格是敌人
边界情况: 无

(4) 计算顺序(消除冗余优化空间)
• Up[0][0], Up[0][1], …, Up[0][n-1]
• Up[1][0], Up[1][1], …, Up[1][n-1]
• …• Up[m-1][0], Up[m-1][1], …, Up[m-1][n-1]
• 时间复杂度O(MN),空间复杂度O(MN)

拓展到4个方向
• Up[i][j]表示如果(i, j)放一个炸弹向上可以最多炸死多少敌人
• 一共四个方向 • 可以类似地计算Down[i][j], Left[i][j], Right[i][j],注意计算顺序会有改变
• (i, j)如果是空地,放一个炸弹最多炸死的敌人数是:
– Up[i][j] + Down[i][j] + Left[i][j] + Right[i][j]
• 取最大值即可 • 时间复杂度和空间复杂度依然为O(MN)

序列型动态规划

• 给定一个序列
• 动态规划方程f[i]中的下标i表示前i个元素a[0], a[1], …, a[i-1]的某种性质 – 坐标型的f[i]表示以ai
为结尾的某种性质
• 初始化中,f[0]表示空序列的性质 – 坐标型动态规划的初始条件f[0]就是指以a0为结尾的子序列的性质

Paint House

题目链接: link.
• 有一排N栋房子,每栋房子要漆成3种颜色中的一种:红、蓝、绿 • 任何两栋相邻的房子不能漆成同样的颜色 • 第i栋房子染成红色、蓝色、绿色的花费分别是cost[i][0], cost[i][1], cost[i][2]
• 问最少需要花多少钱油漆这些房子

(1) 确定状态:分为最后一步以及子问题
最后一步:最优策略中房子N-1一定染成了红、蓝、绿中的一种,但是相邻两栋房子不能漆成一种颜色 ,所以如果最优策略中房子N-1是红色,房子N-2只能是蓝色或绿色。不知道房子N-2是什么颜色,就把它记录下来,分别记录油漆前N-1栋房子并且房子N-2是红色、蓝色、绿色的最小花费,存在二维数组中。
子问题为:求N-1栋房子并且房子N-2是红色、蓝色、绿色的最小花费
(2) 写状态转移方程
状态:设油漆前i栋房子并且房子i-1是红色、蓝色、绿色的最小花费分别为
f[i][0], f[i][1], f[i][2]

(3) 规定初始条件以及边界情况
初始条件::f[0][0] = f[0][1] = f[0][2] = 0
– 即不油漆任何房子的花费
边界情况:无
(4) 计算顺序(消除冗余优化空间)
初始化f[0][0], f[0][1], f[0][2]
• 计算f[1][0], f[1][1], f[1][2]
• …
• 计算f[N][0], f[N][1], f[N][2]
• 答案是min{f[N][0], f[N][1], f[N][2]}. 时间复杂度O(N), 空间复杂度O(N)

涂房子2(Paint House2)

题目链接: link.
• 题意:
• 有一排N栋房子,每栋房子要漆成K种颜色中的一种
• 任何两栋相邻的房子不能漆成同样的颜色
• 房子i染成第j种颜色的花费是cost[i][j]
• 问最少需要花多少钱油漆这些房子

(1) 确定状态:分为最后一步以及子问题
最后一步:需要记录油漆前i栋房子并且房子i-1 是颜色1, 颜色2, …, 颜色K的最小花费:f[i][1], f[i][2], …, f[i][K]。
子问题为:求N-1栋房子并且房子N-2是是颜色1, 颜色2, …, 颜色K的最小花费
(2) 写状态转移方程
状态:设油漆前i栋房子并且房子i-1是颜色1, 颜色2, …颜色K的最小花费分别为
f[i][1], f[i][2], …, f[i][K]

(3) 规定初始条件以及边界情况
初始条件::f[0][0] = f[0][1] = f[0][2] … f[0][k]= 0
– 即不油漆任何房子的花费
边界情况:无
(4) 计算顺序(消除冗余优化空间)
初始化f[0][0], f[0][1], f[0][2]… … f[0][k]
• 计算f[1][0], f[1][1], f[1][2] … f[0][k]
• …
• 计算f[N][0], f[N][1], f[N][2] … f[N][k]
• 答案是min{f[N][0], f[N][1], f[N][2] … f[N][k]}.

最长连续单调子序列

题目链接: [link](http://www.lintcode.com/en/problem/longest-increasing-continuoussubsequence/).
• 题意: • 给定a[0], …, a[n-1]
• 找到最长的连续子序列i, i+1, i+2, …, j, 使得a[i]<a[i+1]<…<a[j],或者
a[i]>a[i+1]>…>a[j],输出长度j-i+1
• 例子:
• 输入:[5, 1, 2, 3, 4]
• 输出:4 (子序列1, 2, 3, 4)

(1) 确定状态:分为最后一步以及子问题
最后一步:对于最优的策略,一定有最后一个元素a[j],第一种情况:最优策略中最长连续上升子序列就是{a[j]},答案是1,第二种情况,子序列长度大于1,那么最优策略中a[j]前一个元素肯定是a[j-1]. 这种情况一定是a[j-1] < a[j]
子问题为:求以a[j-1]结尾的最长连续上升子序列

(2) 写状态转移方程
状态:设f[j] =以a[j]结尾的最长连续上升子序列的长度

(3) 规定初始条件以及边界情况
初始条件::空
边界情况:
– j>0, 即a[j]前面至少还有一个元素
– a[j] > a[j-1], 满足单调性

(4) 计算顺序(消除冗余优化空间)
• f[j] =以a[j]结尾的最长连续上升子序列的长度
• 计算f[0], f[1], f[2], …, f[n-1]
• 答案是max{f[0], f[1], f[2], …, f[n-1]},算法时间复杂度O(n),空间复杂度O(n)

Decode Ways

题目链接: link.
• 有一段由A-Z组成的字母串信息被加密成数字串
• 加密方式为:A->1, B->2, …, Z->26
• 给定加密后的数字串S[0…N-1],问有多少种方式解密成字母串

(1) 确定状态:分为最后一步以及子问题
最后一步:一定有最后一个字母 代表 A, B, …, 或Z,这个字母加密时变成1, 2, …, 或26
子问题为:设数字串长度为N ,要求数字串前N个字符的解密方式数 ,需要知道数字串前N-1和N-2个字符的解密方式数。

(2) 写状态转移方程
状态:设数字串S前i个数字解密成字母串有f[i]种方式

(3) 规定初始条件以及边界情况
初始条件:f[0] = 1,即空串有1种方式解密
– 即不油漆任何房子的花费
边界情况:如果i = 1, 只看最后一个数字
(4) 计算顺序(消除冗余优化空间)
• f[0], f[1], …, f[N]
• 答案是f[N]
• 时间复杂度O(N), 空间复杂度O(N)

比特位技术(Counting Bits)

题目链接: link.
• 题意:
• 给定N,要求输出0, 1, …, N的每个数的二进制表示里的1的个数
• 例子:
• 输入:5
• 输出:[0, 1, 1, 2, 1, 2]

(1) 确定状态:分为最后一步以及子问题
最后一步:观察这个数最后一个二进制位(最低位),去掉它,看剩下 多少个1
子问题为:在N的二进制去掉最后一位N mod 2,设新的数是Y=(X>>1) (右移一位),要知道Y的二进制表示中有多少1

(2) 写状态转移方程
状态:设f[i]表示i的二进制表示中有多少个1

(3) 规定初始条件以及边界情况
初始条件:f[0] = 0
边界情况:无
(4) 计算顺序(消除冗余优化空间)
• f[0], f[1], f[2], …, f[N]
• 时间复杂度O(N)
• 空间复杂度O(N)

打家劫舍(House Robber)

题目链接: link.
• 题意:
• 有一排N栋房子(0~N-1),房子i里有A[i]个金币 • 一个窃贼想选择一些房子偷金币
• 但是不能偷任何挨着的两家邻居,否则会被警察逮住
• 最多偷多少金币
• 例子:
• 输入:A={3, 8, 4}
• 输出:8 (只偷第二家的金币)

(1) 确定状态:分为最后一步以及子问题

最后一步:窃贼的最优策略中,有可能偷或者不偷最后一栋房子N-1
• 情况1:不偷房子N-1 – 简单,最优策略就是前N-1栋房子的最优策略
• 情况2:偷房子N-1 – 仍然需要知道在前N-1栋房子中最多能偷多少金币,但是,需要保证不偷第 N-2栋房子
子问题:在不偷房子N-2的前提下,在前N-1栋房子中最多能偷多少金币

(2) 写状态转移方程
状态:设f[i]为窃贼在前i栋房子最多偷多少金币

(3) 规定初始条件以及边界情况
初始条件:
– f[0] = 0 (没有房子,偷0枚金币)
– f[1] = A[0]
– f[2] = max{A[0], A[1]}
边界情况:无
(4) 计算顺序(消除冗余优化空间)
• 初始化f[0]
• 计算f[1], f[2], …, f[n]
• 答案是f[n]
• 时间复杂度O(N), 空间复杂度O(1)

打家劫舍 II(House Robber II)

题目链接: link.
• 题意:
• 有一圈N栋房子,房子i-1里有A[i]个金币 • 一个窃贼想选择一些房子偷金币
• 但是不能偷任何挨着的两家邻居,否则会被警察逮住
• 最多偷多少金币
• 例子:
• 输入:A={3, 8, 4}
• 输出:8 (只偷房子1的金币)

(1) 确定状态:分为最后一步以及子问题

圈的情况比序列复杂 • 但是,通过对于房子0和房子N-1不能同时偷的原理,两者相加即可
最后一步:窃贼的最优策略中,有可能偷或者不偷最后一栋房子N-1
• 情况1:不偷房子N-1 – 简单,最优策略就是前N-1栋房子的最优策略
• 情况2:偷房子N-1 – 仍然需要知道在前N-1栋房子中最多能偷多少金币,但是,需要保证不偷第 N-2栋房子
子问题:在不偷房子N-2的前提下,在前N-1栋房子中最多能偷多少金币

(2) 写状态转移方程
状态:设f[i]为窃贼在前i栋房子最多偷多少金币

(3) 规定初始条件以及边界情况
初始条件:
– f[0] = 0 (没有房子,偷0枚金币)
– f[1] = A[0]
– f[2] = max{A[0], A[1]}
边界情况:无
(4) 计算顺序(消除冗余优化空间)
• 初始化f[0]
• 计算f[1], f[2], …, f[n]
• 答案是f[n]
• 时间复杂度O(N), 空间复杂度O(1)

买卖股票的最佳时机(Best Time To Buy And Sell Stock)

题目链接: link.
• 题意:
• 已知后面N天一支股票的每天的价格P0, P1, …, PN-1 • 可以最多买一股卖一股
• 求最大利润 • 例子:
• 输入:[3, 2, 3, 1, 2]
• 输出:1 (2买入,3卖出)
• 从0到N-1枚举j,即第几天卖 • 时刻保存当前为止(即0~j-1天)的最低价格Pi • 最大的Pj- Pi即为答案

买卖股票的最佳时机 III(Best Time To Buy And Sell Stock III)建议回看视频

题目链接: link.
• 题意:
• 给定一支股票N天的价格
• 可以进行最多两次买+两次卖,每次买卖都是一股
• 不能在卖光手中股票前买入,但可以同一天卖完后买入
• 问最大收益
• 例子:
• 输入: [4,4,6,1,1,4,2,5] • 输出:6 (4买入,6卖出,1买入,5卖出,收益为(6-4) + (5-1) = 6)

(1) 确定状态:分为最后一步以及子问题
最后一步:最优策略中,最后一次卖发生在第j天 • 枚举最后一次买发生在第几天
,但是不知道之前有没有买卖过,就记录下来。

子问题:要求f[N][1], …, f[N][5] 需要知道f[N-1][1], …, f[N-1][5]

(2) 写状态转移方程
状态:f[i][j]表示前i天(第i-1天)结束后,在阶段j的最大获利

(3) 规定初始条件以及边界情况
初始条件:
– f[0][1] = 0
– f[0][2] = f[0][3] = f[0][4] = f[0][5] = -∞
边界情况:
• 阶段1, 3, 5: f[i][j] = max{f[i-1][j], f[i-1][j-1] + Pi-1 – Pi-2}
• 阶段2, 4: f[i][j] = max{f[i-1][j] + Pi-1 – Pi-2, f[i-1][j-1], f[i-1][j-2] + Pi-1 – Pi-2}
• 如果j – 1 < 1或j – 2 < 1或i – 2 < 0, 对应项不计入max
(4) 计算顺序(消除冗余优化空间)
• 初始化f[0][1], …, f[0][5]
• f[1][1], …, f[1][5]
• …
• f[N][1], …, f[N][5]
• 时间复杂度:O(N), 空间复杂度:O(N),优化后可以O(1),因为f[i][1…5]
只依赖于f[i-1][1…5]

买卖股票的最佳时机 IV(Best Time To Buy And Sell Stock IV)5阶段拓展到2k+1

题目链接: link.
• 题意:
• 给定一支股票N天的价格
• 可以进行最多K次买+K次卖,每次买卖都是一股
• 不能在卖光手中股票前买入,但可以同一天卖完后买入
• 问最大收益
• 例子:
• 输入: [4,4,6,1,1,4,2,5], K = 2
• 输出:6 (4买入,6卖出,1买入,5卖出,收益为(6-4) + (5-1) = 6)

(1) 确定状态:分为最后一步以及子问题
最后一步:最优策略中,最后一次卖发生在第j天 • 枚举最后一次买发生在第几天
,但是不知道之前有没有买卖过,就记录下来。

子问题:要求f[N][1], …, f[N][2k+1] 需要知道f[N-1][1], …, f[N-1][2k+1]

(2) 写状态转移方程
状态:f[i][j]表示前i天(第i-1天)结束后,在阶段j的最大获利

(3) 规定初始条件以及边界情况
• 刚开始(前0天)处于阶段1 – f[0][1] = 0
– f[0][2] = f[0][3] = … = f[0][2K+1] = +∞
• 阶段1, 3, 5, …, 2K+1: f[i][j] = max{f[i-1][j], f[i-1][j-1] + Pi-1 – Pi-2}
• 阶段2, 4, …, 2K: f[i][j] = max{f[i-1][j] + Pi-1 – Pi-2, f[i-1][j-1], f[i-1][j-2] + Pi-1 – Pi-2} • 如果j – 1 < 1或j – 2 < 1或i – 2 < 0, 对应项不计入max

(4) 计算顺序(消除冗余优化空间)
• 初始化f[0][1], …, f[0][2K+1]
• f[1][1], …, f[1][2K+1]
• …
• f[N][1], …, f[N][2K+1]
• 时间复杂度:O(NK), 空间复杂度:O(NK),优化后可以O(K),因为
f[i][1… 2K+1]只依赖于f[i-1][1… 2K+1]

最长上升子序列(Longest Increasing Subsequence)

题目链接: link.
• 题意:
• 给定a[0], …, a[n-1]
• 找到最长的子序列0<=i1<i2<…<iK<n, 使得a[i1]<a[i2]<…<a[iK],输出K • 例子:
• 输入:[4, 2, 4, 5, 3, 7]
• 输出:4 (子序列2, 4, 5, 7)

(1) 确定状态:分为最后一步以及子问题
最后一步:对于最优的策略,一定有最后一个元素a[j]
• 第一种情况:最优策略中最长上升子序列就是{a[j]},答案是1
• 第二种情况,子序列长度大于1,那么最优策略中a[j]前一个元素是a[i], 并且a[i] < a[j]
子问题:不确定最优策略中a[j]前一个元素a[i]是哪个,需要枚举每个i
• 求以a[i]结尾的最长上升子序列

(2) 写状态转移方程
状态:f[j] =以a[j]结尾的最长上升子序列的长度

(3) 规定初始条件以及边界情况
初始条件:空
边界情况:
– i>=0
– a[j] > a[i], 满足单调性

(4) 计算顺序(消除冗余优化空间)
• f[j] =以a[j]结尾的最长上升子序列的长度
• 计算f[0], f[1], f[2], …, f[n-1]
• 答案是max{f[0], f[1], f[2], …, f[n-1]}
• 算法时间复杂度O(n2),空间复杂度O(n)

俄罗斯套娃信封问题(Russian Doll Envelopes)

题目链接: link.
题意:
• 给定N个信封的长度和宽度
• 如果一个信封的长和宽都分别小于另一个信封的长和宽,则这个信封可
以放入另一个信封
• 问最多嵌套多少个信封
• 例子:
• 输入:[[5,4],[6,4],[6,7],[2,3]]
• 输出:3 ([2,3] => [5,4] => [6,7])

(1) 确定状态:分为最后一步以及子问题
最后一步:设最优策略中最后一个信封,即最外层的信封,是Ei • 考虑次外层信封是哪个–,一定是某个Ej,j < i
子问题:要求以Ei为最外层信封时最多的嵌套层数, 需要知道以Ej为最外层信封时最多的嵌套层数(j < i)

(2) 写状态转移方程
状态:设f[i]表示以Ei为最外层信封时最多的嵌套层数

(3) 规定初始条件以及边界情况
初始条件:空
边界情况:空

(4) 计算顺序(消除冗余优化空间)
• f[0], f[1], …, f[N-1]
• 时间复杂度O(N2),空间复杂度O(N)

双序列型动态规划(特殊,属于序列型的拓展)

最长公共子序列(Longest Common Subsequence)

题目链接: link.
• 题意:
• 给定两个字符串A,B
• 一个字符串的子串是这个字符串去掉某些字符(可能0个)之后剩下的 字符串
• 找到两个字符串的最长公共子串的长度
• 例子:
• 输入:A=“jiuzhang”, B=“lijiang” • 输出:5(最长公共子串是jiang )

(1) 确定状态:分为最后一步以及子问题
最后一步:设A长度是m, B长度是n,观察A[m-1]和B[n-1]这两个字符是否作为一个对子在最优策 略中
情况一:对子中没有A[m-1]
情况二:对子中没有B[n-1]
情况三:对子中有A[m-1]-B[n-1]
子问题:求A[0…m-1]和B[0…n-1]的最长公共子串
(2) 写状态转移方程
状态:设f[i][j]为A前i个字符A[0…i-1]和B前j个字符[0…j-1]的最长公共子串的长度

(3) 规定初始条件以及边界情况
初始条件:
– f[0][j] = 0, j=0…n
– f[i][0] = 0, i=0…m
边界情况:空

(4) 计算顺序(消除冗余优化空间)
• f[0][0], f[0][1], …, f[0][n]
• f[1][0], f[1][1], …, f[1][n]
• …• f[m][0], f[m][1], …, f[m][n] • 答案是f[m][n]
• 时间复杂度(计算步数)O(MN),空间复杂度(数组大小)O(MN)

交叉字符串(Interleaving String)

题目链接: link.
• 题意: • 给定三个字符串A, B, X
• 判断X是否是由A, B交错在一起形成 – 即A是X的子序列,去掉A后,剩下的字符组成B • 例子: • 输入:A=“aabcc” B=“dbbac”, X=“aadbbcbcac” • 输出:True( X=“aadbbcbcac” )

(1) 确定状态:分为最后一步以及子问题
最后一步:假设X是由A和B交错形成的,那么X的最后一个字符X[m+n-1]
– 要么是A[m-1]
• 那么X[0…m+n-2]是由A[0…m-2]和B[0…n-1]交错形成的 – 要么是B[n-1]
• 那么X[0…m+n-2]是由A[0…m-1]和B[0…n-2]交错形成的
子问题:需要知道X[0…m+n-2]是否由A[0…m-2]和B[0…n-1]交错形成,以及
X[0…m+n-2]是否由A[0…m-1]和B[0…n-2]交错形成
(2) 写状态转移方程
状态:设f[i][j]为X前i+j个字符是否由A前i个字符A[0…i-1]和B前j个字符B[0…j-1]交错 形成

(3) 规定初始条件以及边界情况
初始条件:空串由A的空串和B的空串交错形成àf[0][0] = True
边界情况:如果i=0,不考虑情况一;如果j=0,不考虑情况二

(4) 计算顺序(消除冗余优化空间)
• f[0][0], f[0][1], …, f[0][n]
• f[1][0], f[1][1], …, f[1][n]
• …• f[m][0], f[m][1], …, f[m][n] • 答案是f[m][n]
• 时间复杂度(计算步数)O(MN),空间复杂度(数组大小)O(MN),可 以用滚动数组优化空间至O(N)

编辑距离(Interleaving String)

题目链接: link.
• 题意: • 给定两个字符串A,B
• 要求把A变成B,每次可以进行下面一种操作:
– 增加一个字符 – 去掉一个字符 – 替换一个字符
• 最少需要多少次操作,即最小编辑距离
• 例子: • 输入:A=“mart”, B=“karma”
• 输出:3 (m换成k,t换成m,加上a)

(1) 确定状态:分为最后一步以及子问题
最后一步:于是最优策略(以及所有合法策略)最终都是让A的最后一个字符变成B 的最后一个字符
情况一:A在最后插入B[n-1]
情况二:A最后一个字符替换成B[n-1]
情况三:A删掉最后一个字符
情况四:A和B最后一个字符相等
子问题:是求A[0…m-1]和B[0…n-1]的最小编辑距离
(2) 写状态转移方程
状态:设f[i][j]为A前i个字符A[0…i-1]和B前j个字符B[0…j-1]的最小编辑距离

(3) 规定初始条件以及边界情况
初始条件:一个空串和一个长度为L的串的最小编辑距离是L
– f[0][j] = j (j = 0, 1, 2, …, n)
– f[i][0] = i (i = 0, 1, 2, …, m)
边界情况:如果i=0,不考虑情况一;如果j=0,不考虑情况二

(4) 计算顺序(消除冗余优化空间)
• f[0][0], f[0][1], …, f[0][n]
• f[1][0], f[1][1], …, f[1][n]
• …• f[m][0], f[m][1], …, f[m][n] • 答案是f[m][n]
• 时间复杂度(计算步数)O(MN),空间复杂度(数组大小)O(MN)

不同的子序列(Distinct Subsequences)

题目链接: link.
• 题意: • 给定两个字符串A[0…m-1],B[0…n-1]
• 问B在A中出现多少次(可以不连续) • 例子 • 输入:A=“rabbbit”, B=“rabbit”
• 输出:3 – rabbbit
– rabbbit
– rabbbit

(1) 确定状态:分为最后一步以及子问题
最后一步:以B为出发点
情况1:B[n-1] = A[m-1],结成对子
情况2:B[n-1]不和 A[m-1]结成对子
子问题:需要知道B[0…n-1]在A[0…m-2]中出现多少次,以及B[0…n-2]在A[0…m-2]
中出现多少次
(2) 写状态转移方程
状态:设f[i][j]为B前j个字符B[0…j-1]在A前i个字符A[0…i-1]中出现多少次

(3) 规定初始条件以及边界情况
初始条件:
– 如果B是空串,B在A中出现次数是1
– f[i][0] = 1 (i = 0, 1, 2, …, m)
– 如果A是空串而B不是空串,B在A中出现次数是0 – f[0][j] = 0 (j = 1, 2, …, n)
边界情况:

(4) 计算顺序(消除冗余优化空间)
• 初始化f[0][0], f[0][1], …, f[0][n]
• f[1][0], f[1][1], …, f[1][n]
• …• f[m][0], f[m][1], …, f[m][n] • 答案是f[m][n]
• 时间复杂度(计算步数)O(MN),空间复杂度(数组大小)O(MN)

正则表达式(Regular Expression Matching)

题目链接: link.
• 题意:
• 给定两个字符串A,B
• B是一个正则表达式,里面可能含有‘.’和‘
– ‘.’ 可以匹配任何单个字符
– ‘
’ 可以匹配0个或多个前一个字符
• 问A和B是否匹配 • 例子:
– isMatch(“aa”,“a”) → false
– isMatch(“aa”,“aa”) → true
– isMatch(“aaa”,“aa”) → false
– isMatch(“aa”, “a*”) → true
– isMatch(“aa”, “.") → true
– isMatch(“ab”, ".
”) → true
– isMatch(“aab”, “cab”) → true

(1) 确定状态:分为最后一步以及子问题
最后一步:关注最后的字符,主要取决于正则表达式B中最后的字符B[n-1]是什么

子问题:需要知道A前m个字符和B前 n-1个字符, A前m-1个字符和B前n个字符以及A前m个字符和B前n-2个 字符能否匹配
(2) 写状态转移方程
状态:设f[i][j]为A前i个字符A[0…i-1]和B前j个字符B[0…j-1]能否匹配

(3) 规定初始条件以及边界情况
初始条件:
–f[0][0] = TRUE
– f[1][0] = … = f[m][0] = FALSE

(4) 计算顺序(消除冗余优化空间)
• f[0][0], f[0][1], …, f[0][n]
• f[1][0], f[1][1], …, f[1][n]
• …• f[m][0], f[m][1], …, f[m][n] • 答案是f[m][n]
• 时间复杂度(计算步数)O(MN),空间复杂度(数组大小)O(MN)

划分型动态规划

• 常见动态规划类型
• 给定长度为N的序列或字符串,要求划分成若干段
– 段数不限,或指定K段 – 每一段满足一定的性质
• 做法
– 类似于序列型动态规划,但是通常要加上段数信息
– 一般用f[i][j]记录前i个元素(元素0~i-1)分成j段的性质,如最小代价

完全平方数(Perfect Squares)

题目链接: link.
• 题意:
• 给定一个正整数n • 问最少可以将n分成几个完全平方数(1,4,9,…)之和
• 例子:
• 输入:n=13
• 输出:2 (13=4+9)

(1) 确定状态:分为最后一步以及子问题
最后一步:关注最优策略中最后一个完全平方数j2 • 最优策略中n-j2也一定被分成最少的完全平方数之和
子问题:需要知道n-j2最少被分成几个完全平方数之和
(2) 写状态转移方程
状态:设f[i]表示i最少被分成几个完全平方数之和

(3) 规定初始条件以及边界情况
初始条件:f[0] = 0
边界情况:空

(4) 计算顺序(消除冗余优化空间)
• 初始化f[0]
• 计算f[1], …, f[N]
• 答案是f[N]

分割回文串II(Palindrome Partitioning II)

题目链接: link.
• 题意:
• 给定一个字符串S[0…N-1]
• 要求将这个字符串划分成若干段,每一段都是一个回文串(正反看起来一
样)
• 求最少划分几次
• 例子:
• 输入:
– “aab” • 输出:– 1 (划分1次à “aa”, “b”)

(1) 确定状态:分为最后一步以及子问题
最后一步:关注最优策略中最后一段回文串,设为S[j…N-1]
子问题:需要知道S前j个字符[0…j-1]最少可以划分成几个回文串

(2) 写状态转移方程
状态:设S前i个字符S[0…i-1]最少可以划分成f[i]个回文串

(3) 规定初始条件以及边界情况
初始条件:f[0] = 0
边界情况:空

(4) 计算顺序(消除冗余优化空间)
• 计算f[0], f[1], …, f[N]
• 答案是f[N]

书籍复印(Palindrome Partitioning II)

题目链接: link.
• 题意:
• 有N本书需要被抄写,第i本书有A[i]页,i=0, 1, …, N-1 • 有K个抄写员,每个抄写员可以抄写连续的若干本书(例如:第3~5本书,
或者第10本书)
• 每个抄写员的抄写速度都一样:一分钟一页
• 最少需要多少时间抄写完所有的书
• 例子:
• 输入:
– A = [3, 2, 4], K=2
• 输出:– 5 (第一个抄写员抄写第1本和第2本书,第二个抄写员抄写第3本书)

(1) 确定状态:分为最后一步以及子问题
最后一步:最优策略中最后一个抄写员Bob(设他是第K个)抄写的部分
– 一段连续的书,包含最后一本,如果Bob抄写第j本到第N-1本书
子问题:需要知道前面K-1个人最少需要多少时间抄完前j本书(第0~j-1本书)

(2) 写状态转移方程
状态:设f[k][i]为k个抄写员最少需要多少时间抄完前i本书

(3) 规定初始条件以及边界情况
初始条件:
• f[0][0] = 0, f[0][1] = f[0][2] = … = f[0][N] = +∞
• f[k][0] = 0 (k > 0)
边界情况:空

(4) 计算顺序(消除冗余优化空间)
• 计算f[0][0], f[0][1], …, f[0][N]
• 计算f[1][0], f[1][1], …, f[1][N]
• …• 计算f[K][0], f[K][1], …, f[K][N] • 答案是f[K][N]
• 时间复杂度O(N2K), 空间复杂度O(NK),优化后可以达到O(N)
• 如果K>N,可以赋值KßN

区间型动态规划

• 给定一个序列/字符串,进行一些操作
• 最后一步会将序列/字符串去头/去尾
• 剩下的会是一个区间[i, j]
• 状态自然定义为f[i][j],表示面对子序列[i, …, j]时的最优性质

最长的回文序列(Longest Palindromic Subsequence)

题目链接: link.
• 题意:
• 给定一个字符串S,长度是N • 找到它最长的回文子序列的长度
• 例子:
• 输入:
– “bbbab”
• 输出:
– 4 (“bbbb”)

(1) 确定状态:最优策略产生最长的回文子串T,长度是M
• 情况1:回文串长度是1,即一个字母
• 情况2:回文串长度大于1,那么必定有T[0]=T[M-1]
•设T[0]是S[i], T[M-1]是S[j]
• T剩下的部分T[1…M-2]仍然是一个回文串,而且是S[i+1…j-1]的最长
回文子串
子问题为:求之前的最长回文串
(2) 写状态转移方程
状态:设f[i][j]为S[i…j]的最长回文子串的长度

(3) 规定初始条件以及边界情况
初始条件:
– f[0][0] = f[1][1] = … = f[N-1][N-1] = 1 • 一个字母也是一个长度为1的回文串
– 如果S[i] == S[i+1], f[i][i+1] = 2
– 如果S[i] != S[i+1], f[i][i+1] = 1
边界情况:无

(4) 计算顺序(消除冗余优化空间)
• 长度1:f[0][0], f[1][1], f[2][2], …, f[N-1][N-1]
• 长度2: f[0][1], …, f[N-2][N-1]
• …• 长度N: f[0][N-1]
• 答案是f[0][N-1]
• 时间复杂度O(N2),空间复杂度O(N2)

扰乱字符串(Longest Palindromic Subsequence)

题目链接: link.
• 题意:
• 给定一个字符串S,按照树结构每次二分成左右两个部分,直至单个字符
• 在树上某些节点交换左右儿子,可以形成新的字符串
• 判断一个字符串T是否由S经过这样的变换而成
• 例子:
• 输入:S=“great” T=“rgtae” • 输出:True

(1) 确定状态:T如果长度和S不一样,那么肯定不能由S变换而来
• 如果T是S变换而来的,并且我们知道S最上层二分被分成S=S1S2,那么
一定有:
– T也有两部分T=T1T2,T1是S1变换而来的,T2是S2变换而来的
– T也有两部分T=T1T2,T1是S2变换而来的,T2是S1变换而来的
子问题为:需要知道T1是否由S1变换而来的,T2是否由S2变换而来
(2) 写状态转移方程
状态:f[i][j][k][h]表示T[k…h]是否由S[i…j]变换而来

(3) 规定初始条件以及边界情况
初始条件:
如果S[i]=T[j],f[i][j][1]=True,否则f[i][j][1]=False
边界情况:无

(4) 计算顺序(消除冗余优化空间)
• 设f[i][j][k]表示S1能否通过变换成为T1 – S1为S从字符i开始的长度为k的子串
– T1为T从字符j开始的长度为k的子串
• 按照k从小到大的顺序进行计算
– f[i][j][1],0<=i<N, 0<=j<N
– f[i][j][2],0<=i<N-1, 0<=j<N-1 – …– f[0][0][N]
• 答案是f[0][0][N]
• 时间复杂度O(N4),空间复杂度O(N3)

戳气球(Burst Balloons)

题目链接: link.
• 题意:
• 给定N个气球,每个气球上都标有一个数字:a1, a2, …, aN
• 要求扎破所有气球,扎破第i个气球可以获得a[left]a[i]a[right]枚金币
– left和right是与i相邻的下标 – 扎破气球i以后,left和right就变成相邻的气球
• 求最多获得的金币数(设a[0]=a[N+1]=1) • 例子:
• 输入:[3, 1, 5, 8]
• 输出:167
– [3,1,5,8]-》 [3,5,8] -》 [3,8] -》 [8] -》 [] – 金币315 + 358 + 138 + 181 = 167

(1) 确定状态:
• 最后一步:一定有最后一个被扎破的气球,编号是i • 扎破i时,左边是气球0,右边是气球N+1,获得金币1*ai
*1=ai • 此时气球1i-1以及i+1N都已经被扎破,并且已经获得对应金币
子问题为:需要知道扎破1 -i-1号气球,最多获得的金币数和扎破i+1~N号气球,最多获得的金币数
(2) 写状态转移方程
状态:设f[i][j]为扎破i+1~j-1号气球,最多获得的金币数

(3) 规定初始条件以及边界情况
初始条件:
f[0][1] = f[1][2] = … = f[N][N+1] = 0
边界情况:无

(4) 计算顺序(消除冗余优化空间)
• 区间动态规划:按照长度j-i从小到大的顺序去算
– f[0][1], f[1][2], f[2][3], …, f[N][N+1]
– f[0][2], f[1][3], f[2][4], …, f[N-1][N+1] – …– f[0][N+1]
• 时间复杂度O(N3),空间复杂度O(N2)

背包型动态规划

• Backpack 可行性背包
– 题面:要求不超过Target时能拼出的最大重量
– 记录f[i][w]=前i个物品能不能拼出重量w

• Backpack V, Backpack VI, 计数型背包
– 题面:要求有多少种方式拼出重量Target
– 记录f[i][w]=前i个物品有多少种方式拼出重量w

• Backpack II, Backpack III, 最值型背包
– 题面:要求能拼出的最大价值
– 记录f[i][w]=前i个/种物品拼出重量w能得到的最大价值
• 关键点
– 最后一步
• 最后一个背包内的物品是哪个
• 最后一个物品有没有进背包
– 数组大小和最大承重Target有关

背包问题(Backpack)

题目链接: link.
• 题意: • 给定N个物品,重量分别为正整数A0,A1, …, AN-1
• 一个背包最大承重是正整数M
• 最多能带走多重的物品
• 例子:
• 输入:4个物品,重量为2, 3, 5, 7. 背包最大承重是11
• 输出:10 (三个物品:2, 3, 5)

(1) 确定状态:分为最后一步以及子问题
最后一步:最后一个物品(重量AN-1)是否进入背包
情况一:如果前N-1个物品能拼出W,当然前N个物品也能拼出W
情况二:如果前N-1个物品能拼出W- AN-1 ,再加上最后的物品AN-1 ,拼出W

子问题为:需要知道前N-1个物品能不能拼出重量0, 1, …, M
(2) 写状态转移方程
状态:设f[i][w] = 能否用前i个物品拼出重量w (TRUE / FALSE)

(3) 规定初始条件以及边界情况
初始条件:
– f[0][0] = TRUE: 0个物品可以拼出重量0
– f[0][1…M] = FALSE: 0个物品不能拼出大于0的重量
边界情况:
– f[i-1][w-Ai-1]只能在w≥Ai-1时使用

(4) 计算顺序(消除冗余优化空间)
• 初始化f[0][0], f[0][1], …, f[0][M]
• 计算前1个物品能拼出哪些重量:f[1][0], f[1][1], …, f[1][M]
• 计算前2个物品能拼出哪些重量:f[2][0], f[2][1], …, f[2][M]
• …
• 计算前N个物品能拼出哪些重量:f[N][0], f[N][2], …, f[N][M]
• 时间复杂度(计算步数):O(MN),空间复杂度(数组大小):优化后
可以达到O(M)

背包问题V(BackpackV)

题目链接: link.
• 题意:
• 给定N个正整数:A0,A1, …, AN-1
• 一个正整数Target
• 求有多少种组合加起来是Target
• 每个Ai只能用一次
• 例子:
• 输入:A=[1, 2, 3, 3, 7], Target=7
• 输出:2 (7=7, 1+3+3=7)

(1) 确定状态:分为最后一步以及子问题
最后一步:第N个物品(重量AN-1)是否进入背包
情况一:用前N-1个物品拼出W
情况二:用前N-1个物品能拼出W- AN-1 ,再加上最后的物品AN-1 ,拼出W
情况一的个数+情况二的个数=用前N个物品拼出W的方式

子问题为:需要知道前N-1个物品有多少种方式拼出拼出重量0, 1, …, Target
(2) 写状态转移方程
状态:设f[i][w] = 用前i个物品有多少种方式拼出重量w

(3) 规定初始条件以及边界情况
初始条件:
– f[0][0] = 1: 0个物品可以有一种方式拼出重量0
– f[0][1…M] = 0: 0个物品不能拼出大于0的重量
边界情况:
– f[i-1][w-Ai-1]只能在w≥Ai-1时使用

(4) 计算顺序(消除冗余优化空间)
• 初始化f[0][0], f[0][1], …, f[0][Target]
• 计算前1个物品有多少种方式拼出重量:f[1][0], f[1][1], …, f[1][Target]
• …
• 计算前N个物品有多少种方式拼出重量:f[N][0], f[N][2], …, f[N][Target]
• 答案是f[N][Target]
• 时间复杂度(计算步数):O(N*Target),空间复杂度(数组大小):优
化后可以达到O(Target)

背包问题II(BackpackII)

题目链接: link.
• 题意:
• 给定N个物品,重量分别为正整数A0,A1, …, AN-1,价值分别为正整数V0,
V1, …, VN-1
• 一个背包最大承重是正整数M
• 最多能带走多大价值的物品
• 例子:
• 输入:4个物品,重量为2, 3, 5, 7,价值为1, 5, 2, 4. 背包最大承重是11
• 输出:9 (物品一+物品三,重量3+7=10,价值5+4=9)

(1) 确定状态:分为最后一步以及子问题
最后一步:最后一个物品(重量AN-1, 价值VN-1)是否进入背包
选择一:如果前N-1个物品能拼出W,最大总价值是V,前N个物品也能拼出
W并且总价值是V
选择二:如果前N-1个物品能拼出W- AN-1,最大总价值是V,则再加上最后一
个物品(重量AN-1, 价值VN-1),能拼出W,总价值是V+VN-1

子问题为:需要知道前N-1个物品能不能拼出重量0, 1, …, M,以及拼出重量W能获得的最大价值

(2) 写状态转移方程
状态:设f[i][w] = 用前i个物品拼出重量w时最大总价值(-1表示不能拼出
w)

(3) 规定初始条件以及边界情况
初始条件:
–f[0][0] = 0: 0个物品可以拼出重量0,最大总价值是0
– f[0][1…M] = -1: 0个物品不能拼出大于0的重量
边界情况:
–f[i-1][w-Ai-1]只能在w≥Ai-1,并且f[i-1][w-Ai-1] ≠-1时使用

(4) 计算顺序(消除冗余优化空间)
• 初始化f[0][0], f[0][1], …, f[0][M]
• 计算前1个物品拼出各种重量的最大价值:f[1][0], f[1][1], …, f[1][M]
• …
• 计算前N个物品拼出各种重量的最大价值:f[N][0], f[N][2], …, f[N][M]
• 答案:max0<=j<=M{f[N][j] | f[N][j] ≠-1}
• 时间复杂度(计算步数):O(MN),空间复杂度(数组大小):优化后
可以达到O(M)

背包问题III(BackpackIII)在二的基础上不限个数

题目链接: link.
• 题意:
• 给定N种物品,重量分别为正整数A0,A1, …, AN-1,价值分别为正整数V0, V1, …, VN-1 • 每种物品都有无穷多个
• 一个背包最大承重是正整数M • 最多能带走多大价值的物品
• 例子:
• 输入:4个物品,重量为2, 3, 5, 7,价值为1, 5, 2, 4. 背包最大承重是10
• 输出:15 (3个物品一,重量33=9,价值53=15)

(1) 确定状态:分为最后一步以及子问题
最后一步:最后一个物品(重量AN-1, 价值VN-1)是否进入背包
选择一:如果前N-1个物品能拼出W,最大总价值是V,前N个物品也能拼出
W并且总价值是V
选择二:如果前N-1个物品能拼出W- AN-1,最大总价值是V,则再加上最后一
个物品(重量AN-1, 价值VN-1),能拼出W,总价值是V+VN-1

子问题为:需要知道前N-1个物品能不能拼出重量0, 1, …, M,以及拼出重量W能获得的最大价值

(2) 写状态转移方程
状态:设f[i][w] = 用前i个物品拼出重量w时最大总价值(-1表示不能拼出
w)

优化

(3) 规定初始条件以及边界情况
初始条件:
–f[0][0] = 0: 0个物品可以拼出重量0,最大总价值是0
– f[0][1…M] = -1: 0个物品不能拼出大于0的重量
边界情况:
–f[i-1][w-Ai-1]只能在w≥Ai-1,并且f[i-1][w-Ai-1] ≠-1时使用

(4) 计算顺序(消除冗余优化空间)
• 初始化f[0][0], f[0][1], …, f[0][M]
• 计算前1个物品拼出各种重量的最大价值:f[1][0], f[1][1], …, f[1][M]
• …
• 计算前N个物品拼出各种重量的最大价值:f[N][0], f[N][2], …, f[N][M]
• 答案:max0<=j<=M{f[N][j] | f[N][j] ≠-1}
• 时间复杂度(计算步数):O(MN),空间复杂度(数组大小):优化后
可以达到O(M)

博弈型动态规划

• 博弈为两方游戏
• 一方先下,在一定规则下依次出招
• 如果满足一定条件,则一方胜
• 目标:取胜

Jump game

题目链接: link.
给出一个非负整数数组,你最初定位在数组的第一个位置。数组中的每个元素代表你在那个位置可以跳跃的最大长度。判断你是否能到达数组的最后一个位置。
(1) 确定状态:分为最后一步以及子问题
最后一步:如果青蛙能跳到最后一块石头n-1,我们考虑它跳的最后一步,这一步是从石头i跳过来,i<n-1。需要满足两种条件:
– 青蛙可以跳到石头i – 最后一步不超过跳跃的最大距离:n-1-i<=ai

子问题为:青蛙能不能跳到石头i
(2) 写状态转移方程
OR代表枚举

(3) 规定初始条件以及边界情况
初始条件::f[0] = True,因为青蛙一开始就在石头0
边界情况:无
(4) 计算顺序(消除冗余优化空间)
初始化f[0]=True
• 计算f[1], f[2], …, f[n-1]

硬币排成线(Coins in A Line)

题目链接: link.
• 题意:
• 有一排N个石子,Alice, Bob两人轮流取石子
• 每次一个人可以从最右边取走1个或2个石子
• 取走最后石子的人胜 • 问先手Alice是否必胜 (先手必胜: true,先手必败: false)
• 例子:
• 输入:N=5
• 输出:true (先手取走2个石子,剩下3个石子,无论后手怎么拿,先手
都可以取走最后一个石子)

(1) 确定状态:分为最后一步以及子问题
最后一步:博弈动态规划通常从第一步分析,而不是最后一步,面对N个石子,是否先手必胜

子问题为:知道面对N-1个石子和N-2个石子,是否先手必胜
(2) 写状态转移方程
状态:设f[i]表示面对i个石子,是否先手必胜(f[i] = TRUE / FALSE)

(3) 规定初始条件以及边界情况
初始条件:
• f[0] = FALSE — 面对0个石子,先手必败
• f[1] = f[2] = TRUE — 面对1个石子或2个石子,先手必胜
边界情况:无
(4) 计算顺序(消除冗余优化空间)
• f[0], f[1], f[2], …, f[N]
• 如果f[N] = TRUE先手必胜,否则先手必败
• 时间复杂度O(N)
• 空间复杂度O(N),可以滚动数组优化至O(1)

硬币排成线III(Coins in A LineIII)

题目链接: link.
• 题意:
• 给定一个序列a[0], a[1], …, a[N-1]
• 两个玩家Alice和Bob轮流取数
• 每个人每次只能取第一个数或最后一个数
• 双方都用最优策略,使得自己的数字和尽量比对手大
• 问先手是否必胜
– 如果数字和一样,也算先手胜 • 例子:
• 输入:[1, 5, 233, 7] • 输出:True (先手取走1,无论后手取哪个,先手都能取走233)

(1) 确定状态:分为最后一步以及子问题
最后一步:博弈动态规划通常从第一步分析,而不是最后一步,面对N个石子,是否先手必胜

子问题为:目标同样是最大化先手(自己)和后手(Alice)的数字差
(2) 写状态转移方程
状态:设f[i][j]为一方先手在面对a[i…j]这些数字时,能得到的最大的与对
手的数字差

(3) 规定初始条件以及边界情况
初始条件:
• f[i][i] = a[i] (i=0, 1, …, N-1)
边界情况:无
(4) 计算顺序(消除冗余优化空间)
• 长度1:f[0][0], f[1][1], f[2][2], …, f[N-1][N-1]
• 长度2: f[0][1], …, f[N-2][N-1]
• …• 长度N: f[0][N-1]
• 如果f[0][N-1]>=0,先手Alice必赢,否则必输
• 时间复杂度O(N2),空间复杂度O(N2)

动态规划Dynamic programming笔记自用相关推荐

  1. 动态规划 dynamic programming

    动态规划dynamic programming June,7, 2015 作者:swanGooseMan 出处:http://www.cnblogs.com/swanGooseMan/p/455658 ...

  2. 动态规划(Dynamic Programming)的一些事一些情

    References <算法导论> 最近在回顾算法的知识,特将一些动态规划的重点记录下来,好让以后自己不要忘记. 基本概念 动态规划(Dynamic Programming,简称为DP,下 ...

  3. 运筹学状态转移方程例子_动态规划 Dynamic Programming

    从运筹学和算法的角度综合介绍动态规划 规划论 Mathematical Programming / Mathematical Optimization In mathematics, computer ...

  4. 动态规划|Dynamic Programming

    由于最近课设要用动态规划,翻阅资料学习一下. 动态规划 解决复杂问题的方法,把它们分解成更简单的子问题. 一旦我们看到一些例子,这个定义就有意义了.实际上,我们今天只看解问题的例子 解决DP问题的步骤 ...

  5. 动态规划(Dynamic Programming)与贪心算法(Greedy Algorithm)

    文章目录 动态规划算法(Dynamic Programming) 动态规划问题的属性 应用实例:最长公共子序列问题(Longest Common Subsequence, LCS) 贪心算法(Gree ...

  6. [欠驱动机器人]4,动态规划(Dynamic Programming)

    目录 前言 控制问题变成优化问题 新增成本(Additive cost) 图搜索的最优控制 连续动力学方程 HJB 方程 求出最小控制 数值求解J 方程逼近与数值迭代 线性方程逼近 网格上的值迭代 连 ...

  7. 算法导论-动态规划(dynamic programming)

    动态规划:通过组合子问题的解来解决整个问题. 动态规划的四个步骤: 1)描述最优解的结构: 2)递归定义最优解的值: 3)按自低向上的方式计算最优解的值(首先找到子问题的最优解,解决子问题,最后找到问 ...

  8. 关于简单动态规划(Dynamic Programming)的总结

    Instructions 综上所述(好像没有上)我的DP真的垃圾的一批... 动态规划是用来避免重复计算状态导致效率低的情况,实现动规有记忆化搜索和填表两种方法,但记忆化搜索不能优化空间,所以常用的是 ...

  9. 算法初探-动态规划(Dynamic Programming)

    编程之中接触到很多关于算法的知识,想来整理一番,算是对自己记忆的一个提醒 1.Coin11Problem  问题为:使用最少的三种面值为1,3,5的硬币组合出11元. 重要的是状态以及状态转移方程 d ...

  10. 动态规划(Dynamic Programming)例题步骤详解

    文章目录 动态规划(Dynamic Programming)浅学 - 学习笔记 题目特点: 1.选择硬币组合问题:(Coin Change) 动态规划题四个核心步骤: 一.确定状态 二.转移方程 三. ...

最新文章

  1. vs code中使用Astyle排版C/C++代码
  2. Swift基础语法 、 元组(Tuple)
  3. linux下mysql数据库操作命令
  4. Bootstrap 导入js文件,浏览器找不到文件问题
  5. html textarea粘贴事件,javascript在textarea中捕获粘贴事件
  6. 修炼成QTP高手的十个步骤
  7. 《穿越计算机的迷雾》读书笔记七
  8. matlab三轴机械臂,MATLAB 中的机械臂算法——路径规划
  9. c语言error lnk 2005,易语言5.71静态编译问题LIBC.lib(crt0dat.obj) : error LNK2005:
  10. 基于Android 平台简易即时通讯的研究与设计
  11. php关闭notice_php的notice怎么关闭
  12. 身份证复印件正确签注写法 -- 很重要!
  13. python 日期运算_Python中关于日期的计算总结
  14. 访问activemqProblem accessing /. Reason: Service Unavailable Powered by Jett
  15. 基于FBX SDK的FBX模型解析与加载 -(三)
  16. 【优化模型】图论与TSP模型结合
  17. 亚马逊、速卖通、wish、Lazada、shoppe、ebay、煤炉测评跟淘宝shua单区别在哪?
  18. 暑期练习web25:web code(i春秋)index.php文件包含、base64图片加密
  19. E: Unable to locate package XXX 的解决办法
  20. 蓝桥杯 算法提高 回文串

热门文章

  1. Flask中自定义红图拆分视图函数的方法以及——为什么蓝图不适合用于拆分试图函数
  2. 交换机、路由器、网关
  3. debian10 buster 在thinkpad T470P 上安装指纹驱动 138a:0097 Validity Sensors
  4. 纯粹的Pure Storage,简单却又不简单
  5. matlab模拟嫦娥奔月,【文章】仿真动画软件设计作品--嫦娥奔月
  6. 对话冉小波:NULS三年来的实战心得与反思 |链捕手
  7. Cordys BOP 4.3平台使用小经验—数据库连接管理、导出流程模型为XPDL
  8. 超全总结!一文囊括李航《统计学习方法》几乎所有的知识点!
  9. 泰然金融牵手快公益 花儿计划国学课入畲乡
  10. 慕课网前端JavaScript面试(4)