希望用一种规律搞定背包问题
零钱兑换(题目链接)
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
你可以认为每种硬币的数量是无限的。
解题思路
存储结构: 数组dp[i]表示凑成总金额为i所需的最少的硬币个数,长度为amoun+1
怎么解决不能组合成的情况: 将dp数组每个值都初始化为amount+1,若不能组合成,dp值会一直为amount+1,只需在最后返回的时候判断下就好
状态转移: dp[i] = min(dp[i-coins[j])+1(j是遍历coins数组的下标)
从0->amount,dp[i-coins[j])表示这一步加的是coins[j],1就表示加入了一个硬币
初始化: dp[0] = 0
参考题解:
https://leetcode-cn.com/problems/coin-change/solution/322-ling-qian-dui-huan-by-leetcode-solution/(理解动态规划过程)
https://leetcode-cn.com/problems/coin-change/solution/322-ling-qian-dui-huan-dong-tai-gui-hua-e2nt7/(理解不能组合成的情况)
代码
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
int len = coins.size();
int dp[amount+1];
for(int i=0;i<amount+1;i++){
dp[i] = amount+1; //初始化
}
dp[0] = 0;
for(int i=1;i<=amount;i++){
for(int j=0;j<len;j++){
if(i-coins[j]>=0){
dp[i] = min(dp[i],dp[i-coins[j]]+1); //状态转移方程
}
}
}
return dp[amount]==amount+1?-1:dp[amount]; //检查能否组合成
}
};
组合总和Ⅳ(题目链接)
给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。
题目数据保证答案符合 32 位整数范围。
解题思路
跟上一题很像,只不过这个是让求出总的组合个数
状态转移方程: dp[i] +=dp[i-nums[j]]
注意: 当i-nums[i] = 0的时候应该是+1
代码
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
double dp[target+1];
for(int i=0;i<=target;i++){
dp[i] = 0;
}
for(int i=1;i<=target;i++){
for(int j=0;j<nums.size();j++){
if(i-nums[j]>=0){
if(i-nums[j]==0) dp[i]+=1;
else dp[i]+=dp[i-nums[j]];
}
}
}
return dp[target];
}
};
最小划分(题目链接)
给出一个正整数数组,写一个程序把这个整数数组分成S1跟S2两部分,使S1中的和跟S2中的和的差的绝对值最小。换句话讲,如果有一个一个整数数组 S 有 n 个数,如果Subset1有 m 个数,Subset2必须有 n-m 个数并且 abs(sum(Subset1) – sum(Subset2)) 应该最小
解题思路
0-1背包问题的变形(这竟然也是,绝绝子!)
0-1背包问题知识补充
先求出数组元素的总和sum,将数组中的元素看作重量和价值相同的物品,来装容量为sum/2的背包,背包能装的最大价值就是相差绝对值最小的其中一个数组的元素之和res,最后返回|sum-res-res|
注意: 0-1背包问题要先遍历物品,再遍历背包容量,且要从后往前遍历(保证每个物品只能用一次)
代码
class Solution {
public:
int findMin(vector<int> &nums) {
int len = nums.size();
if(len==0) return 0;
int sum = 0;
for(int i=0;i<len;i++){ //求sum
sum += nums[i];
}
int num = sum/2;
vector<int> dp(num+1,0); //动态规划数组
for(int i=0;i<len;i++){ //先遍历物品(nums数组)
for(int j=num;j>=nums[i];j--){ //再遍历容量,且要从后往前遍历
dp[j] = max(dp[j-nums[i]]+nums[i],dp[j]); //状态转移方程
}
}
return abs(sum-2*dp[num]);
}
};