一、基础知识
动态规划的问题经常要分类讨论,这是因为动态规划的问题本来就有「最优子结构」的特点,即大问题的最优解通常由小问题的最优解得到。
理解重点:就是当前的dp是否能通过之前的dp推出来
关键 1:理解题意
题目要我们找出和最大的连续子数组的值是多少,「连续」是关键字,连续很重要,不是子序列。
题目只要求返回结果,不要求得到最大的连续子数组是哪一个。这样的问题通常可以使用「动态规划」解决。
关键 2:如何定义子问题(如何定义状态)
设计状态思路:把不确定的因素确定下来,进而把子问题定义清楚,把子问题定义得简单。找到子问题之间的联系。动态规划的思想通过解决了一个一个简单的问题,进而把简单的问题的解组成了复杂的问题的解。
子问题 1:以−2 结尾的连续子数组的最大和是多少;
子问题 2:以1 结尾的连续子数组的最大和是多少;
子问题 3:以−3 结尾的连续子数组的最大和是多少;
子问题 4:以4 结尾的连续子数组的最大和是多少;
子问题 5:以−1 结尾的连续子数组的最大和是多少;
子问题 6:以2 结尾的连续子数组的最大和是多少;
子问题 7:以1 结尾的连续子数组的最大和是多少;
子问题 8:以−5 结尾的连续子数组的最大和是多少;
子问题 9:以4 结尾的连续子数组的最大和是多少。
如果编号为 i 的子问题的结果是负数或者 0 ,那么编号为 i + 1 的子问题就可以把编号为 i 的子问题的结果舍弃掉(这里 i 为整数,最小值为 1 ,最大值为 8)
关键3:返回值
返回值不一定是dp[length]
以n=3为例,根据头结点划分左右结点个数: 当1为结点时,左边结点0个,右边结点2个 = dp[0] * dp[2] 当2为结点时,左边结点1个,右边结点1个 = dp[1] * dp[1] 当3为结点时,左边结点2个,右边结点0个 = dp[2] * dp[0]
所以以dp[3]都可以通过dp[0]、dp[1]、dp[2]得到
递推公式:遍历头结点j(从1到n):dp[i] = dp[j-1] * dp[i-j]
二、动态规划问题分类
2.1 0-1背包问题
背包最大重量为4。 物品为: 重量 价值 物品0 1 15 物品1 3 20 物品2 4 30 问背包能背的物品最⼤价值是多少?
dp[i] [j] 表示从下标为[0-i]的物品⾥任意取,放进容量为j的背包,价值总和最⼤是多少。
不放物品i:由dp[i - 1][j]推出,即背包容量为j,⾥⾯不放物品i的最⼤价值,此时dp[i][j]就是dp[i - 1][j]。(其 实就是当物品i的重量⼤于背包j的重量时,物品i⽆法放进背包中,所以背包内的价值依然和前⾯相同。) 放物品i:由dp[i - 1][j-weight[i]]推出,dp为背包容dp[i - 1][j-weight[i]]量为j - weight[i]的时候不放物品i的最⼤价值,那么dp[i - 1][j-weight[i]]+ value[i] (物品i的价值),就是背包放物品i得到的最⼤价值 所以递归公式: dp[i][j] = max(dp[i - 1][j], dp[i−1][j-weight[i]]+ value[i]);
遍历顺序:从背包或者物品都可以
// weight数组的⼤⼩ 就是物品个数
for(int i = 1; i < weight.size(); i++) { // 遍历物品
for(int j = 0; j <= bagweight; j++) { // 遍历背包容量
if (j < weight[i]) dp[i][j] = dp[i - 1][j];
else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
}
一维dp数组(滚动数组)
如果dp[i-1]那一层拷贝到dp[i]上,可以化简为
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
遍历顺序:只能是先便利物品,再遍历背包,而且遍历背包时,需要倒序遍历(为了保证物品i只被放⼊⼀次!)
for(int i = 0; i < weight.size(); i++) { // 遍历物品
for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
0-1背包问题延申(两个维度的重量)
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
int dp[][] = new int[m+1][n+1];
for (int i = 0; i < strs.length; i++) {
char[] chars = strs[i].toCharArray();
int one = 0;//记录每个物品的重量(one)
int zero = 0;//记录每个物品的重量(zero)
for (int j = 0; j < chars.length; j++) {
if(chars[j] == '1')one++;
if(chars[j] == '0')zero++;
}
for (int j = m; j >= zero; j--) {
for (int k = n; k >= one; k--) {
dp[j][k] = Math.max(dp[j][k],dp[j-zero][k-one]+1);
}
}
}
return dp[m][n];
}
}
0-1背包问题变形(取与不取、分为两堆问题)
取与不取问题如果用回溯算法,则是指数级别的,用动态规划则是o(n方)