我觉得动态规划主要有两点:
1. 当前这个点要与不要
2. 由小推向大(也就是所谓子问题),以空间换时间。
01背包
例:有5个物品,w = [2,2,6,5,4],v = [6,3,5,4,6],背包的容量为10。
1. 一个物品一个物品的慢慢放
2. 拿到一个物品到底是放还是不放
当前物品不放,则价值为上一次放的情况;如果放了,则价值为把前i-1个物品放入(当前容量-这个物品容量)的价值加当前物品的价值。显然,谁大要谁。
| | 0|1 | 2 | 3 | 4|5 | 6 |7 | 8 | 9 |10
|–|–|–|–|–|–|–|–|–|–|–|–|–|–|–|–|
|0 |0 |0 |0 | 0 | 0 | 0 |0 | 0 | 0 | 0 |0 |
| 1 | 0 | 0 |6 |6 | 6 | 6 |6 | 6 | 6 | 6 |6 |
| 2 | 0 |0 | ? | | | | | | | | | | | | | | | | |
| 3 | 0 | 0 | | | | | | | | | | | | | | | | | |
| 4 | 0 | 0 | | | | | | | | | | | | | | | | | |
| 5 | 0 | 0 | | | | | | | | | | | | | | | | | |
初始化:i个物品装入容量0的背包中,价值肯定0;0个物品放入容量j的背包,价值也是0 。
dp[1][?]处放第一个物品,放不下,价值是放0物品时得0;放进去价值是6;
dp[2][?]处放第二个物品,放不下,价值和上一行的一样;?处放得下,放,价值为前(2-1)个物品放入(2-2)容量中,得,0+3 = 3;不放,就是上一次的6,;所以?处不放新物品。
以此类推。。。
最后放到第5个物品,便能得到最大价值。
1 | /** |
最长公共子序列
例:给定两个字符串 “abcbdb”和 “acbbabdbb”,返回这两个字符串的最长公共子序列的长度。
1. 从字符串开头慢慢比
2. 当前字符相等了,公共长度则为上一次的加1;不等了,要么不要字符1要么不要字符2,显然,谁的结果长要谁。
| | |”” |a | c | b | b|a | b |d | b| b |
|–|–|–|–|–|–|–|–|–|–|–|–|–|–|
| | |0|1 | 2 | 3 | 4|5 | 6 |7 | 8 | 9 |
|”” |0 |0 |0 |0 | 0 | 0 | 0 |0 | 0 | 0 | 0 |0 |
|a| 1 | 0 | 1 |1 |1 | 1 | 1 |1 | 1 | 1 | 1
|b| 2 | 0 |1 | ? | | | | | | | | | | | | | | | | |
|c| 3 | 0 | 1 | | | | | | | | | | | | | | | | | |
|b| 4 | 0 | 1 | | | | | | | | | | | | | | | | | |
|d| 5 | 0 | 1 | | | | | | | | | | | | | | | | | |
|b| 6 | 0 | 1 | | | | | | | | | | | | | | | | | |
初始化:有一方为空,公共长度都是0;
dp[1][?]处第一个字符的比较结果,a==a,则长度为:上一次(即左上对角线)[0][0]+1 = 1; a != c,不要a(即上一行)则长度为0;不要c(即左一列)则长度为1。所以公共长度为1
dp[2][?]处第二个字符的比较结果,b != a,不要b(即上一行),长度为1;不要a(即左一列)长度为0 。所以公共长度为1 。
以此类推。。。
最后字符比较完,便能得到最长结果。
1 | public int longestCommonSubsequence(String str1, String str2) { |
编辑距离
例:给你两个单词 word1 = “horse”和word2 = “ros”,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
1. 从字符串开头慢慢比
2. 当前字符相等了,则编辑距离和上一次一样;不等了,则进行如下的三种编辑,显然,谁小要谁。
你可以对一个单词进行如下三种操作:
- 插入一个字符
- 删除一个字符
- 替换一个字符
| | |”” |h | o | r | s | e|
|–|–|–|–|–|–|–|–|–|
| | |0|1 | 2 | 3 | 4|5 | 6 |7 | 8 | 9 |
|”” |0 |0 |1 |2 | 3 | 4 | 5|
|r| 1 | 1 | 1|?
|o| 2 | 2 |1 | | | | |
|s| 3 | 3 | 1 | | | | | |
初始化:一个字符编辑成一个字符串,这个简单。
dp[1][?]处第一个字符的编辑距离,r != h,则进行编辑:不要r(即上一行)(对于另一个字符串而言就是加上h,效果一样)则编辑距离为1+1=2;不要h(即左一列)则编辑距离为1+1=2。替换,编辑距离为上一次加1,即0+1 = 1 。所以编辑距离为1,替换一次就行。
以此类推。。。
最后字符比较完,便能得到最小编辑距离。
1 | public int minDistance(String word1, String word2) { |
打家劫舍
例:如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。房屋存放金额[1,2,3,1],计算不触动警报装置的情况下 ,能够偷窃到的最高金额。
1. 一间一间慢慢偷
2. 当前房屋到底偷还是不偷。
当前房屋偷了,则价值上上一次的价值加这一次的价值;如果不偷,则价值为上一次的价值。显然,谁大要谁。
1 | public int rob(int[] nums) { |
零钱兑换
例:给定不同面额的硬币 coins = [1, 2, 5]和一个总金额 amount = 11。计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
1. 一块钱一块钱的慢慢换
2. 当前这个硬币到底要不要。
当前硬币不要,则个数没变化;如果要,则个数变为兑换(要兑换的钱减去当前这个面额)的个数加1。显然,谁小要谁。
1 | public static int coinChange(int[] coins, int amount) { |