题目1:416 分割等和子集
题目链接:416 分割等和子集
题意
将包含正整数的非空数组分割成两个子集,使得两个子集中的元素和相等,如果可以返回true,否则返回false
数组中的元素小于等于100 数组中元素个数小于等于200 所以dp数组内部是0~10000
每个元素只可使用1次 本题转化为01背包问题 数组中的元素能否装满容量为sum/2的背包 重量和价值等价
dp[sum/2] == (sum/2)
动态规划
1)明确dp数组及下标j的含义
dp[j] 容量为j的背包所能装的最大价值
2)dp数组初始化
容量为0的背包所能装的最大价值 dp[0] = 0
其他非零容量的背包所能装的最大价值 dp[j] = 0 根据递推公式,将其设为非负整数的最小值0
3)递推公式
weight[i] == value[i] == nums[i]
dp[j] = max(dp[j],dp[j-numst[i]]+nums[i])
4)遍历顺序
正序遍历物品,倒序遍历背包
正序遍历nums, 倒序遍历nums
for(int i=0;i<nums.size();i++){
for(int j=(sum/2);j>=nums[i];j--){
}}
5)打印dp数组
代码
class Solution {
public:
bool canPartition(vector<int>& nums) {
//定义dp数组 初始化dp数组
vector<int> dp(10001, 0);
int sum = 0;
for(int i=0;i<nums.size();i++){
sum += nums[i];
}
//剪枝
if(sum % 2) return false;
int target = sum / 2;
//遍历顺序
//先遍历物品,后遍历倒序背包
for(int i=0;i<nums.size();i++){
for(int j=target;j>=nums[i];j--){
dp[j] = max(dp[j], dp[j-nums[i]]+nums[i]);
}
}
if(dp[target] == target) return true;
return false;
}
};
- 时间复杂度:O(n^2)
- 空间复杂度:O(n),虽然dp数组大小为一个常数,但是大常数
题目2:1049 最后一块石头的重量Ⅱ
题目链接:1049 最后一块石头的重量Ⅱ
题意
整数数组元素stones[i]表示第i块石头的重量
每次任选2块石头粉碎 两石头的重量 x<=y
当x==y 两块石头被完全粉碎; 当x!=y 重量为x的石头会完全粉碎,重量为y的石头新重量是y-x
最多只会剩一块石头,返回此石头的最小可能重量;若无石头剩下,就返回0
数组中的元素小于等于100 数组中元素个数小于等于30 所以dp数组内部是0~1500
每块石头只使用1次,01背包问题 本题和上一题分割等和子集是一个求法,也是把数组分成两组,让两组尽可能地相等,这样取得的差最小 求得装满背包容量为(sum/2)的背包的最大价值
最终求 sum-dp[sum/2]-dp[sum/2]即可
动态规划
1)明确dp数组及下标j的含义
dp[j] 装满容量为j的背包,最多装的重量是dp[j]
2)dp数组初始化
背包容量为0时,dp[0] = 0
背包容量不为0时,更加递推公式,初始化dp[j]为0 dp[j]=0
3)递推公式
dp[j] = max(dp[j],dp[j-stones[i]]+stones[i])
4)遍历顺序
先遍历物品 后倒序遍历背包
for(int i=0;i<stones.size();i++){
for(int j=(sum/2);j>=stones[i];j--){
}}
5)打印dp数组
代码
class Solution {
public:
int lastStoneWeightII(vector<int>& stones) {
//定义dp数组 初始化dp数组
vector<int> dp(1501,0);
int sum = 0;
for(int i=0;i<stones.size();i++){
sum += stones[i];
}
int target = sum / 2;
//遍历顺序
//先遍历物品,后倒序遍历背包
for(int i=0;i<stones.size();i++){
for(int j=target;j>=stones[i];j--){
dp[j] = max(dp[j], dp[j-stones[i]]+stones[i]);
}
}
int ret = sum - dp[target];
if(ret-dp[target]) return abs(ret-dp[target]);
return 0;
}
};
- 时间复杂度:O(n × m) , n为石头块数 m是石头总重量的一半,
- 空间复杂度:O(m)
题目3:494 目标和
题目链接:494 目标和
题意
非负整数数组nums 在数组中的每个整数前添加‘+’ ‘-’串联起所有整数,构造一个表达式
返回等于表达式结果等于target的不同表达式的数目
分成两组,一组是加法的集和left,一组是减法的集和right
left+right=sum ①
left-right=target ②
由①得 right=sum-left ③
将③代入②得 left-(sum-left)=target
式中只有1个变量left left=(target+sum)/2 找出元素和组成为left的情况dp[left] 每个元素只能使用1次 01背包 装满容量为left的背包有几种方法
如果(sum+target)不能被2整除,那么就不能找到一个left (理由:将①式和②式相加,得2*left=sum+target 要想找到一个left,(sum+target)一定要被整除)
还有就是 abs(target)>sum 避免出现数组中只有1个元素,而该元素的绝对值还小于abs(target)的情况 例如 nums=[100] target=-200
动态规划
1)明确dp数组及下标i的含义
dp[j] 找出装满背包容量为j的背包,有dp[j]种方法
2)dp数组初始化
装满容量为0的背包,有dp[0]种方法 dp[0]=1
3)递推公式
dp[j] += dp[j-nums[i]]
4)遍历顺序
先正序遍历物品,后倒序遍历背包
for(int i=0;i<nums.size();i++){
for(int j=left;j>=nums[i];j--){
}}
5)打印dp数组
代码
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
//定义dp数组
vector<int> dp(20001, 0);
//初始化dp数组
dp[0] = 1;
int sum = 0;
for(int i=0;i<nums.size();i++){
sum += nums[i];
}
if((sum + target) % 2) return 0;
if(abs(target) > sum) return 0;
int left = (sum + target) / 2;
//遍历顺序
//先遍历物品,后倒序遍历背包
for(int i=0;i<nums.size();i++){
for(int j=left;j>=nums[i];j--){
dp[j] += dp[j-nums[i]];
}
}
return dp[left];
}
};
- 时间复杂度:O(n × m),n为数组中元素的个数,m为背包容量
- 空间复杂度:O(m),m为背包容量
题目4:474 一和零
题目链接:474 一和零
题意
返回二进制字符串数组strs 的最大子集长度,子集中最多有m个0和n个1
若x的所有元素也是y的元素,集合x是集合y的子集
两个维度(0 1)的背包且每个元素只能使用1次 01背包 二维dp数组 dp[m][n] 物品的重量就是x个0 y个1
动态规划
1)明确dp数组及下标j的含义
dp[i][j] 装满i个0和j个1最多背了dp[i][j]个物品
2)dp数组初始化
背包容量为00时,不装东西 dp[0][0]=0
背包容量不为0时,根据递推公式,dp[i][j]=0
3)递推公式
装x个0 y个1 将物品放进来(个数+1)
dp[i][j] = max(dp[i][j], dp[i-x][j-y]+1)
4)遍历顺序
先遍历物品,再倒序遍历背包
for(string str:strs){//遍历物品
//统计该字符串的0的个数和1的个数
int x,y=0;
for(char c:str){
if(c=='0') x++;
if(c=='1') y++;}
for(int i=m;i>=x;i--){//遍历背包容量
for(j=n;j>=y;j--){
}
}
}
}
5)打印dp数组
代码
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
//定义dp数组 初始化dp数组
vector<vector<int>> dp(m+1,vector<int>(n+1,0));
//遍历顺序
//先遍历物品,后倒序遍历背包
for(string str : strs){//遍历物品
int x = 0;
int y = 0;
//获取0和1的个数
for(char c : str){
if(c == '0') x++;
if(c == '1') y++;
}
//倒序遍历背包
for(int i=m;i>=x;i--){
for(int j=n;j>=y;j--){
dp[i][j] = max(dp[i][j], dp[i-x][j-y]+1);
}
}
}
return dp[m][n];
}
};
- 时间复杂度: O(kmn),k 为strs的长度
- 空间复杂度: O(mn)