这是我参与「第四届青训营 」笔记创作活动的第[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)。