题目来源
题目描述
题目解析
思路一
(1)定义状态
-
d
p
[
i
]
[
k
]
[
j
]
dp[i][k][j]
dp[i][k][j]表示前i个数中取出一部分构成
j
j
j的方案个数,而且该方案中元素个数为k
- i: 0 ~ n
- j: 0 ~ target
- k: 0 ~ target
(2)转移方程
- d p [ i ] [ k ] [ j ] = d p [ i − 1 ] [ k − m ] [ j − m ∗ A [ i − 1 ] ] ∗ C ( k , m ) + d p [ i − 1 ] [ k ] [ j ] , 1 < = m < = k dp[i][k][j] = dp[i - 1][k - m][j - m * A[i - 1]] * C(k, m) + dp[i - 1][k][j], 1 <= m <= k dp[i][k][j]=dp[i−1][k−m][j−m∗A[i−1]]∗C(k,m)+dp[i−1][k][j],1<=m<=k
- C(i, j)表示i个数中选j个数的组合个数
(3)初始条件
- d p [ i ] [ 0 ] [ 0 ] = 1 dp[i][0][0] = 1 dp[i][0][0]=1
- d p [ 0 ] [ l ] [ j ] = 0 dp[0][l][j] = 0 dp[0][l][j]=0
(4)答案
- sum(dp[n][k][target]), k: 1 ~ target
思路二
注意到题目要求中,数的顺序不同也被认为是不同的组合(更确切地说,这是一个排列问题)那么我们之前通过先循环物品来避免重复的做法就不能使用了,反而这题应该反过来,先循环背包容量,再循环物品,这样我们就可以满足题目中”数的顺序不同则被认为是不同的组合“的要求,此时的状态定义也不需要前 i 个物品这个维度了,只需要一个一维数组 f,f[i] 表示正好装满容量i的组合个数
class Solution {
public:
int backPackVI(vector<int> &nums, int target) {
// write your code here
vector<int> dp(target + 1, 0);
dp[0] = 1;
for (int i = 1; i <= target ; i++) {
for (int j = 0; j < nums.size(); j++) {
if (i - nums[j] >= 0) {
dp[i] += dp[i - nums[j]];
}
}
}
return dp[target];
}
};
class Solution {
std::unordered_map<int, int> memo;
int dfs(vector<int> &nums, int target){
if(target == 0){
return 1;
}
if(memo.count(target)){
return memo[target];
}
int ans = 0;
for(int num : nums){
if(target >= num){
ans += dfs(nums, target - num);
}
}
return memo[target] = ans;
}
public:
int backPackVI(vector<int> &nums, int target) {
return dfs(nums, target);
}
};