动态规划(DP) | 青训营笔记

113 阅读2分钟

这是我参与「第四届青训营 」笔记创作活动的第[8]天

动态规划

  • 动态规划适合用来求解最优问题,比如最大值,最小值等。
  • 动态规划可以将已经计算过的结果再次利用,从而减少计算量。
  • 它可以非常显著的降低时间复杂度,提高代码的运行效率。

0-1背包问题

  • 动态规划的经典题目,那就背包问题,在有限的空间内,拿取最多的物品。

  • 我们可以使用一个二维数组,将背包抽象化。

    // weight:物品的重量 n:物品的个数  w:背包可承载重量
     public int dp(int[] weight, int n, int w) {
         boolean[][] arr = new boolean[n][w + 1];
         arr[0][0] = true; //第一行的数据要特殊处理, 可以利用哨兵优化
         arr[0][weight[0]] = true;
         for (int i = 1; i < n; i ++) { //动态规划状态转移
             for (int j = 1; j <= w; j ++) {  //不把第i个物品装入背包
                 if(arr[i - 1][j] == true) arr[i][j] = arr[i - 1][j];
             }
             for (int j = 1; j <= w - weight[i]; j ++) {  //把第i个物品装入背包
                 if (arr[i - 1][j] == true) arr[i][j + weight[i]] = true;
             }
         }
         for (int i = w; i >= 0; i --) {  //最后输出结果
             if (arr[n - 1][i] == true) return i;
         }
         return 0;
     }
        
    
  • 动态规划就是将一个问题分解为多个阶段,每个阶段对应一个决策,我们记录每一个阶段可达的状态集合(去除重复的),然后通过当前阶段的状态集合,来推到下一个阶段的状态集合,动态的向前推进。

  • 这个问题也可以通过递归来实现,使用回溯算法来穷举所有的可能性,将所有的可能性计算出来,从中找到最优解,但是回溯算法的时间复杂度非常高,是指数级的。

  • 但是通过这次动态规划的解法,可以将回溯算法进行优化,优化成动态规划差不多的时间复杂度。

  • 动态规划是将计算过的结果保存下来,避免重复计算,那么回溯也可以这样,只需要每次将计算的结果保存下来,计算之前先从结果集里面查找是否计算过,如果计算过那么就跳过,没有计算过就计算。

时间复杂度

  • 在以上代码中,有两层for循环,第一层循环中,需要执行n次,而在第二层循环中需要执行w次,然而它的时间复杂度也就是O(n * w)。

空间复杂度

  • 在以上代码中,定义了一个二位数组arr,它的大小是n * w + 1, 定义的i, j可以不计入,那么它的空间复杂度也就是O(n * w)。