leetcode 494. 目标和
题目链接:目标和
本题要如何使表达式结果为target:
既然为target,那么就一定有 left组合 - right组合 = target
left + right = sum,而sum是固定的。right = sum - left,所以left - (sum - left) = target 推导出 left = (target + sum)/2 。
此时问题就转化为:装满容量为left的背包,有几种方法
本题是求解装满一个背包有几种方法,属于组合问题,与之前的背包问题不同。
- 确定dp数组以及下标的含义
dp[j]:填满j(包括j)这么大容积的包,有dpj种方法 - 确定递推公式
以dpj,j 为5为例:
已经有1(numsi)的话,有 dp4种方法凑成容量5的背包
已经有2(numsi)的话,有 dp3种方法凑成容量5的背包
已经有3(numsi)的话,有 dp2中方法凑成容量5的背包
已经有4(numsi)的话,有 dp1中方法凑成容量5的背包
已经有5(numsi)的话,有 dp0中方法凑成容量5的背包
只要有了numsi,凑成dpj就有dpj - nums\[i] 种方法,凑成dp5就是把所有的dpj - nums\[i] 累加起来。所以求组和类的递归公式:dp[j] += dp[j - nums[i]] - dp数组初始化
dp[0]初始化为1 - 确定遍历顺序
nums放在外循环,target在内循环,且内循环倒序
cpp
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
int sum = 0;
for (int i = 0; i < nums.size(); i++) sum += nums[i];
if (abs(target) > sum) return 0; // 此时没有方案
if ((target + sum) % 2 == 1) return 0; // 此时没有方案
int bagSize = (target + sum) / 2;
vector<int> dp(bagSize + 1, 0);
dp[0] = 1;
for (int i = 0; i < nums.size(); i++) {
for (int j = bagSize; j >= nums[i]; j--) {
dp[j] += dp[j - nums[i]];
}
}
return dp[bagSize];
}
};
时间复杂度:O(n × m),n为正数个数,m为背包容量
空间复杂度:O(m),m为背包容量
leetcode 474. 一和零
题目链接:一和零
本题中strs 数组里的元素就是物品,每个物品都是一个!
m 和 n相当于是一个背包,两个维度的背包
- dp数组及下标的含义
dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dpij。 - 确定递推公式
dpij 可以由前一个strs里的字符串推导出来,strs里的字符串有zeroNum个0,oneNum个1。
dpij 就可以是 dpi - zeroNumj - oneNum + 1。然后在遍历的过程中,取dpij的最大值。
所以递推公式:dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
字符串的zeroNum和oneNum相当于物品的重量(weighti),物品的重量有两个维度。字符串本身的个数相当于物品的价值(valuei)。 - dp数组初始化
物品价值不会是负数,初始为0,保证递推的时候dpij不会被初始值覆盖。 - 遍历顺序
外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历。
cpp
for (string str : strs) { // 遍历物品
int oneNum = 0, zeroNum = 0;
for (char c : str) {
if (c == '0') zeroNum++;
else oneNum++;
}
for (int i = m; i >= zeroNum; i--) { // 遍历背包容量且从后向前遍历
for (int j = n; j >= oneNum; j--) {
dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
}
}
}
整体代码如下:
cpp
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
vector<vector<int>> dp(m + 1, vector<int> (n + 1, 0));
for (string str : strs) {
int oneNum = 0, zeroNum = 0;
for (char c : str) {
if (c == '0') zeroNum++;
else oneNum++;
}
for (int i = m; i >= zeroNum; i--) {
for (int j = n; j >= oneNum; j--) {
dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
}
}
}
return dp[m][n];
}
};
时间复杂度: O(kmn),k 为strs的长度
空间复杂度: O(mn)