这道题和之前的不太一样,乍一看可以拿组合总和问题一样,想尝试回溯法,发现超时,于是我们可以用动态规划,这时我们要考虑dp数组的定义是什么
我们假设加法的和为x,那减法的和就是sum-x,可以推出x-(sum-x) = target,举个例子,以示例1为例,tar=3,加法和为2的话,一减,减法和就是1,所以x要减(sum-x)才是target,这里要注意,不是加,还有就是nums都要参与运算,不是取几个构造也要注意,可以推出x = (sum+target)/2;x为背包容量,所以dp的定义为填满容量为x的背包有几种方法,此时有人考虑到x会不会向下取整,对的,所以如果(sum + target)%2!=0,返回0。
递推公式:举个例子,dp[4] 有几个,我们首先要拿到一个数比如我们拿到nums[0]=1,那么我们只
需求dp[3]即可,dp[3]有几个,dp[4]由他们加上1得来,再拿一个数为2,dp[4]再加上dp[1]的个数即可
所以递推公式为:dp[j] += dp[j - nums[i]];
初始化:dp都由dp[0]推导而来,dp[0]必须为1,不然递归都是0,很好理解,填满容量为0的背包
有两种方法,就是放0件物品
遍历顺序先遍历物品,背包容量大到小
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int n = nums.length;
int sum = 0;
for(int i = 0;i < n;i++) {sum += nums[i];}
int bag = (sum + target) / 2;
if(Math.abs(target) > sum) {return 0;}
if((sum + target) % 2 ==1){return 0;}
int[] dp = new int[bag + 1];
dp[0] = 1;
for(int i = 0;i < n;i++) {
for(int j = bag;j >= nums[i];j--) {
dp[j] += dp[j - nums[i]];
}
}
return dp[bag];
}
}
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
//这道题其实是一个01背包问题,因为strs相当于物品,每个字符串都是一个物品,每个物品都只有一个
//而m,n相当于背包,只不过这个背包有两个维度而已,理解了题意,那这道题就可以开始做了
//dp数组的定义,dp[i][j],表示最多有i个0,j个1的strs的子集最大长度
//递推公式:定义0的个数numOf0,1的个数numOf1,dp[i][j] 由dp[i - numOf0][j - numOf1] + 1和
//它本身比大小得来,这里的意思是,举个例子,循环拿物品,我拿了”10“,有1个0,1个1,我们之后的
//dp都带上这个物品一起玩,把它的个数减掉求到的dp的值加上它本身的长度1,再和之前一比,谁大,那新的dp[i][j]就是谁,循环遍历strs,得到的dp[m][n]就是最长的子集
//所以dp[i][j] = Math.max(dp[i][j],dp[i - numOf0][j - numOf1] + 1);
//初始化:0即可
//遍历顺序:
int[][] dp = new int[m + 1][n + 1];
for(String string:strs) {
int numOf0 = 0;
int numOf1 = 0;
for(int i = 0;i < string.length();i++) {
if(string.charAt(i)=='0') numOf0++;
else numOf1++;
}
for(int i = m;i >= numOf0;i--) {
for(int j = n;j >= numOf1;j--) {
dp[i][j] = Math.max(dp[i][j],dp[i - numOf0][j - numOf1] + 1);
}
}
}
return dp[m][n];
}
}
这两道题还是很难的,有些同学可能觉得写出递推公式就行了,其实递推公式只是一小部分,你需要理解dp数组的定义,初始化,遍历的顺序,我觉得最重要的是怎样理解题意,把题转换成背包的格式,什么当作物品,什么是背包,再根据物品数量的特性决定是01背包,还是完全背包,还是别的,理解比什么都重要,剩下的就是多练习找感觉了。