LC 1049. 最后一块石头的重量 II
题目链接:LC 1049. 最后一块石头的重量 II
思路:和分割等和子集很像,都是将数组分成两组,让这两组数之和的差尽可能的小。
代码:
class Solution {
public:
//本质就是将石头分成两组,两组互相抵消,得到最小的重量
//与上一章节的分割等子集类似,都是将数组分成尽可能相似的两组
int lastStoneWeightII(vector<int>& stones) {
int sum = 0;
for(int a : stones){
sum += a;
}
int target = sum/2;//只需下取整,得到的值都是比较小的,最后让sum-target得到的值*2,即可
vector<int> dp(target+1);//使用一维dp数组
//dp数组的[i][j]表示在从0-i中选容量最大为j的物品的价值(价值和重量相同,但是不一定能沾满背包)
//dp数组初始化
for(int b=0; b<dp.size(); b++){
if(b>=stones[0])dp[b] = stones[0];
else dp[b] = 0;
}
//更新数组
for(int i=1; i<stones.size(); i++){
for(int j=dp.size()-1; j>=stones[i]; j--){
dp[j] = max(dp[j], dp[j-stones[i]]+stones[i]);
}
}
int result = sum - dp.back() - dp.back();
return result;
}
};
LC 494. 目标和
题目链接:LC 494. 目标和
思路:先按着二维的dp数组进行推导。然后转化为一维数组。主要思路就是,将数组分成两部分,一部分是加,一部分是减,加和减能通过输入确定下来,然后只需选加的部分有几种情况就可以。
代码:
class Solution {
public:
//按照背包的思路解
//将数组分成两组:一组是加的,一组是减的,jia—jian = target
//jia+jain = sum,其中sum和target是已知的,所以jia = (target+sum)/2
int findTargetSumWays(vector<int>& nums, int target) {
int sum = 0;
for(int a:nums){
sum += a;
}
int x = target + sum;
if(x%2 == 1)return 0;//当x是奇数的时候,直接返回
x = x/2;
if(abs(target)>sum)return 0;//当target太大或者太小时,就直接返回
//将不能满足的情况删除后,就判断多少组合之和为x
//dp纵坐标含义为子数组组合之和,dp内的值表示可通过之前的值组合得到列值的个数
vector<int> dp(x+1, 0);
//初始化dp
dp[0] = 1;//子数组之和为0,就一种情况所有的都不选
//更新dp
for(int i=0; i<nums.size(); i++){
for(int j=dp.size()-1; j>=nums[i]; j--){
dp[j] = dp[j]+dp[j-nums[i]];
}
}
return dp.back();
}
};
LC 474.一和零
题目链接:LC 474.一和零
思路:之前是一维的滚动数组,这道题有两个约束,所以用二维滚动数组,数组内的值表示最大的子集数量,当加入一个新字符串时,判断子集上次或者加这次之后,哪个子集长一些,哪个长就选择哪个。
代码:
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
vector<vector<int>> dp(m+1, vector<int>(n+1));//二维dp数组,值为最大的子集个数
for(string s:strs){
//判断每个字符串有多少0和1
int zero = 0;
int one = 0;
for(char c : s){
if(c=='0')zero++;
if(c=='1')one++;
}
for(int i=dp.size()-1; i>=zero; i--){//m
for(int j=dp[0].size()-1; j>=one; j--){//n
dp[i][j] = max(dp[i][j], dp[i-zero][j-one]+1);
}
}
}
return dp[m][n];
}
};