【25届秋招备战C++】题型练习-背包问题
- 0-1背包
- 416 - 分割等和子集
- 1049 - 最后一块石头的重量 Ⅱ
- 494 - 目标和
- 474- 一和零
- 完全背包
- 518- 零钱兑换Ⅱ
- 377- 组合总数Ⅱ
- 322- 零钱兑换
- 279- 完全平方数
- 参考
0-1背包
416 - 分割等和子集
链接: 分割等和子集
解题思路:给定容量判断是否可以满足固定的价值
分割等和问题,只需要求和取一半作为结果即可,总和为奇直接返回false,此题关键在于背包价值的遍历顺序,代码随想录的解释(每一个元素一定是不可重复放入,所以从大到小遍历)很妙
1.确定dp数组以及下标的含义
dp[j] 表示:j容量能装的最大价值,有dp[j]种方法
2.确定递推公式
dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]) 装满j容量的方法数统一公式
3.dp数组如何初始化
dp[0]=1,非零下标初始为0
4.确定遍历顺序
对于01背包问题一维dp的遍历,nums放在外循环,target在内循环,且内循环倒序。
class Solution {
public:bool canPartition(vector<int>& nums) {vector<int> dp(10001,0);int sum=0;for(int i=0;i<nums.size();i++){sum +=nums[i];}if (sum % 2 == 1) return false;sum=sum/2;for(int i=0;i<nums.size();i++){for(int j=sum;j>=nums[i];j--){dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]);}}if(dp[sum]==sum){return true;}return false;}
};
1049 - 最后一块石头的重量 Ⅱ
链接: 最后一块石头的重量 Ⅱ
解题思路:其实就是分割等和的变体,只要找到尽可能平分的背包即可。
class Solution {
public:int lastStoneWeightII(vector<int>& stones) {vector<int> dp(15001, 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]);}}return sum - dp[target] - dp[target];}
};
494 - 目标和
链接: 目标和
解题思路:总容量固定的方法数
分为两个子集,两包和为target,设其中一包的和为x,则另一包的和为sum-x,且x-(sum-x)=target,即x=(taget+sum)/2,即只需要求解总容量为(target+sum)/2的可能方法数
1.确定dp数组以及下标的含义
dp[j] 表示:装满j容量,有dp[j]种方法
2.确定递推公式
dp[j] += dp[j - nums[j]] 装满j容量的方法数统一公式
3.dp数组如何初始化
dp[0]=1,非零下标初始为0
4.确定遍历顺序
对于01背包问题一维dp的遍历,nums放在外循环,target在内循环,且内循环倒序。
class Solution {
public:int findTargetSumWays(vector<int>& nums, int S) {int sum = 0;for (int i = 0; i < nums.size(); i++) sum += nums[i];if (abs(S) > sum) return 0; // 此时没有方案if ((S + sum) % 2 == 1) return 0; // 此时没有方案int bagSize = (S + 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];}
};
474- 一和零
链接: 一和零
解题思路:满足最多m个0,n个1最多有多少个物品
1.确定dp数组以及下标的含义
dp[i][j] 表示:i个0,j个1,最多装dp[i][j]个物品
2.确定递推公式
dp[i][j]=max(dp[i-x][j-y]+1,dp[i][j]) (每个物品的重量是x个0y个1)
3.dp数组如何初始化
dp[0][0]=0,非零下标初始为0
4.确定遍历顺序
对于01背包问题二维dp的遍历,nums放在外循环,target在内循环,且内循环倒序。
先遍历物品再遍历背包
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 x=0,y=0;for(char c:str){if(c=='0') x++;else 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];}
};
完全背包
518- 零钱兑换Ⅱ
链接: 零钱兑换Ⅱ
解题思路:思路同目标和,注意初始化dp[0]=1其实是为了迎合递推公式,没有实际意义
1.确定dp数组以及下标的含义
dp[j] 表示:装满j容量,有dp[j]种方法
2.确定递推公式
dp[j] += dp[j - nums[j]] 装满j容量的方法数统一公式
3.dp数组如何初始化
dp[0]=1,非零下标初始为0
4.确定遍历顺序
完全背包正序,先物品再背包不会重复是组合数,先背包再物体有顺序是排列数。
class Solution {
public:int change(int amount, vector<int>& coins) {vector<int> dp(amount+1,0);dp[0]=1;for(int i=0;i<coins.size();i++){for(int j=coins[i];j<=amount;j++){dp[j]+=dp[j-coins[i]];}}return dp[amount];}
};
377- 组合总数Ⅱ
链接: 组合总数Ⅱ
解题思路:思路同零钱兑换Ⅱ,唯一不同是本题强调顺序注意初始化dp[0]=1其实是为了迎合递推公式,没有实际意义
1.确定dp数组以及下标的含义
dp[j] 表示:装满j容量,有dp[j]种方法
2.确定递推公式
dp[j] += dp[j - nums[j]] 装满j容量的方法数统一公式
C++测试用例有两个数相加超过int的数据,所以需要在if里加上dp[i] < INT_MAX - dp[i - num]。
3.dp数组如何初始化
dp[0]=1,非零下标初始为0
4.确定遍历顺序
完全背包正序,先物品再背包不会重复是组合数,先背包再物体有顺序是排列数。
class Solution {
public:int change(int amount, vector<int>& coins) {vector<int> dp(amount+1,0);dp[0]=1;for(int i=0;i<coins.size();i++){for(int j=coins[i];j<=amount;j++){dp[j]+=dp[j-coins[i]];}}return dp[amount];}
};
322- 零钱兑换
链接: 零钱兑换
解题思路:思路同零钱兑换Ⅱ,但这里是最小值,则初始化时需要给定INT_MAX,且递推公式也要用min取最小
1.确定dp数组以及下标的含义
dp[j] 表示:装满j容量,最少有dp[j]个物品
2.确定递推公式
dp[j] = min(dp[j - nums[j]]+1,dp[j])
C++测试用例有两个数相加超过int的数据,所以需要在if里加上dp[i] < INT_MAX - dp[i - num]。
3.dp数组如何初始化
dp[0]=1,非零下标初始为0
4.确定遍历顺序
完全背包正序,先物品再背包不会重复是组合数,先背包再物体有顺序是排列数。
class Solution {
public:int coinChange(vector<int>& coins, int amount) {vector<int> dp(amount + 1, INT_MAX);dp[0] = 0;for (int i = 0; i < coins.size(); i++) { for (int j = coins[i]; j <= amount; j++) { if (dp[j - coins[i]] != INT_MAX) { dp[j] = min(dp[j - coins[i]] + 1, dp[j]);}}}if (dp[amount] == INT_MAX) return -1;return dp[amount];}
};
279- 完全平方数
链接: 完全平方数
解题思路:思路同零钱兑换,但这里是最小值,则初始化时需要给定INT_MAX,且递推公式也要用min取最小
1.确定dp数组以及下标的含义
dp[j] 表示:装满j容量,最少有dp[j]个物品
2.确定递推公式
dp[j] = min(dp[j - nums[j]]+1,dp[j])
C++测试用例有两个数相加超过int的数据,所以需要在if里加上dp[i] < INT_MAX - dp[i - num]。
3.dp数组如何初始化
dp[0]=1,非零下标初始为0
4.确定遍历顺序
完全背包正序,先物品再背包不会重复是组合数,先背包再物体有顺序是排列数。
public:int numSquares(int n) {vector<int> dp(n + 1, INT_MAX);dp[0] = 0;for (int i = 0; i <= n; i++) { // 遍历背包for (int j = 1; j * j <= i; j++) { // 遍历物品dp[i] = min(dp[i - j * j] + 1, dp[i]);}}return dp[n];}
};
参考
代码随想录救我狗命!!!!!!!!
链接: 代码随想录