Skip to content

递推公式是动态规划的灵魂,dp的含义、初始化以及遍历的顺序也很关键,但最重要是分析问题的思想,在此,将动态规划划分为五个阶段,来自于代码随想录

五个阶段
  • 明确dp数组的含义
  • 对dp进行正确初始化
  • 找出递推公式
  • 明确遍历的次序和层级
  • debug
动态规划的优劣
  • 其效率显然是高于单纯的递归算法和暴力枚举的
  • 每次动态规划都需要自定一个dp数组,甚至是二维、三维数组,未免太浪费空间
动态规划的案例
N个矩阵连乘问题
  • 描述:给定n个矩阵{A1,A2,...,An} ,其中 AiAi+1是可乘的。 如何确定矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积所需要的数乘次数最少

  • 分析:根据矩阵连乘的结合律,我们可以将其整个相乘划分为两块甚至更多块相乘,从而得到子问题

    • 我们维护一个数组dp[i:j] 表示其计算量从而可得到 (1) :
    • 根据dp数组找到最优次数
    • 题解代码n_matrix.cpp
    dp(i,j)={0i=jminik<j{dp(i,k)+dp(k+1,j)+pi1pkpj}i<j
最长公共子序列
  • 描述:给定两字符串str1str2,找出其最长公共子序列,找出其最长公共子串

    • 子串:字符串中连续的子集
    • 子序列:将给定的字符串去掉n (n0)个之后的字符串
  • 分析:最长公共子串纯粹凑个热闹,O(n) 即可

    • 边界str1.length()==0str2.length()==0

    • 我们可以追究str1str2[0:n-1] ,n=str2.length的结果从而得到递推公式(2):

      dp(i,j)={0ifi=0orj=0dp(i1,j1)+1ifi,j>0andxi=yjmax(dp(i1,j),dp(i,j1))ifi,j>0andxiyi
  • 题目代码

最大子段和
  • 描述:给定 n 个整数(可以为负数)的序列(a1,a2,...,an) ,求$ \max\lbrace{ 0 ,\max_\limits{1\le i\le j\le n} \sum_\limits{k=i}^{j} a_k }\rbrace$ 实例:[2,11,4,13,5,2]
  • 分析:我们维护一个数组dp[i],显然对于dp[i]有 dp[i]=max(dp[i1]+a[i1],dp[i1]) ,再定res = max(dp[i],dp[i-1]) 遍历即可
  • 题目代码
0-1背包问题
  • 描述:一个小偷在洗劫商铺时找到了n个物品:其中第i个物品价值vi 并且重量为wi(价值和重量整数),小偷背包只能承受W ,物品要么带走,要么留下,求如何才能价值最大化

    • 如果带走的一个物品可以拆分,即只带走一部分,如何价值最大化
  • 分析:考察前i个物品能获得的最高价值 V[i,w] ,w 为背包的承受力可有如(3)

    V[i,w]={max{V[i1,w],V[i1,wwi]+vi}ifwi<wV[i1,w]ifwi>w
  • 题目代码

资源分配问题
  • 描述:有资源n,分配给m个项目,gi(x) 为第i个项目分得资源x所得到的利润。 求总利润最大的资源分配方案,即解下列问题:
    • maxz=g1(x1)+g2(x2)+...+gm(xm),x1+x2+...+xm=a,xi0,i=1,2,3,...,m
    • gi(x) 如下图

资源分配

  • 分析:很容易得到dp[i]=max(dp[i],dp[jw[i]+v[i]]) ,其中 w[i]v[i] 是投入和收益
  • 题目代码: