leetcode刷题记录:动态规划总结

一、0-1背包问题和完全背包问题的异同

相同点:1. 状态和动作相同,动作都是对于第i个物品,选还是不选,dp[i][j]的意义都是,在前i和物品中,背包容量为j时,能装的最大价值是dp[i][j]

              2. 对于第i个物品,如果不选,dp[i][j] 都是等于dp[i - 1][j]

不同点:1. 对于第i个物品,如果选,0-1背包是dp[i - 1][j - wt[i - 1]] + val[i - 1],完全背包是dp[i ][j - wt[i - 1]] + val[i - 1],因为完全背包中物品的数量无限,所以dp[i] = dp[][j - wt[i - 1]] + val[i - 1],表示选了第 i 个物品之后还能选第 i 个。

二、对二维dp数组的遍历方向选择及压缩后的遍历方向选择

不管dp数组是几维的,遍历顺序的选择只需要遵守两个原则:

       这部分可以看东哥的文章:动规答疑篇

  1. 所需的状态是计算过的,对于压缩成一维的dp 数组,要保证所需的状态不会被替换

  例如0-1背包问题:dp[i][j] = min( dp[i - 1][j] + dp[i - 1][j - wt[i - 1]] + val[i - 1]),第 i 行,第 j 列需要上一行第 j - wt[i - 1]列的状态,所以要从上到下,从左到右遍历,但是,如果压缩为1维,要从右到左遍历,因为压缩以后相当于用新值替换旧值,新值相当于第 i 行,旧值相当于 i - 1行,所以在算第 j 列的时候要保证第 j - wt[i - 1]列的旧值不会被替换,所以要从右往左遍历。

  但是对于完全背包问题:dp[i][j] = min( dp[i - 1][j] + dp[i][j - wt[i - 1]] + val[i - 1]),如果是二维的,还是从上到下,从左到右遍历,但是如果是一维的,不再和0-1背包一样是从右往左,而是从左往右,因为算dp[i][j] 需要同一行的dp[i][j - wt[i - 1]],两个都是新值,所以要从左往右。

  2. 遍历的终点就是问题的答案

三、状态转移方程不好定义的话说明思路错了

      比如对于背包问题:dp[i][j] 的定义是在前 i 个物品中,重量不超过 j 时的最大价值,遍历的方向是从前往后,从上往下

   但是对于打家劫舍问题,如果还是用背包问题的思路dp[i] 定义为打劫前 i 个房屋的最大价值,但是要求不能打劫相邻房屋,状态转移方程写不出来,所以应该换个思路,把dp[i] 定义为从i 开始打劫的最大价值,那么不能打劫相邻房屋的要求可以融合在状态转移方程里:dp[i] = max(dp[ i + 1], val[i] + dp[i + 2]),从后往前遍历。

  再比如在一个包含负数的序列中选择最大子序列,使子序列的和最大,dp[i]应为以 nums[i] 结尾的序列的和,这样的话可以判断到底要不要前边的结果,如果dpd[i -1] >= 0,

dp[i] = dp[i - 1] + num[i],否则dp[i] = num[i], 最后遍历dp数组得到答案。dp[i]如果定义成前i个数中的最大序列和,最后的结果只能是数组中所有正数的和。

       总结:dp[i]的定义方式:

  1. 前 i 个数的目标值

  2.从第 i 个数开始的目标值

  3. 以第 i 个数结尾的目标值

posted @ 2020-09-04 13:45  嫩西瓜  阅读(173)  评论(0)    收藏  举报