DP
complete pack
Leetcode 322. Coin Change
如果用1d array的话,0/1背包需要内层需要从大到小循环是因为每个物品只能被添加一次
for example, w[0] = 1, v[0] = 10, if from 0 -> max
dp[1] = dp[1 - w[0]] + v[0] = 10
dp[2] = dp[2 - w[0]] + v[0] = 20
but if from max -> 0
dp[2] = dp[2 - w[0]] + v[0] = 10
dp[1] = dp[1 - w[0]] + v[0] = 10
但是完全背包可以被添加多次,所以不需要从大到小
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
// complete
// meet certain amount
// dp[j] = min(dp[j], dp[j - coins[i]] + 1)
vector<int> dp(amount + 1, amount + 1);
dp[0] = 0;
for (int i = 0; i < coins.size(); i ++)
{
for (int j = coins[i]; j <= amount; j ++)
{
dp[j] = min(dp[j], dp[j - coins[i]] + 1);
}
}
return dp[amount] == amount + 1 ? -1 : dp[amount];
}
};
Leetcode 279. Perfect Squares
class Solution {
public:
int numSquares(int n) {
// sum to n
vector<int> dp(n + 1, n + 1);
dp[0] = 0;
for (int j = 0; j <= n; j ++)
{
for (int i = 1; i * i <= j; i ++)
{
dp[j] = min(dp[j], dp[j - i * i] + 1);
}
}
/*
for (int i = 1; i * i <= n; i ++)
{
for (int j = i * i; j <= n; j ++)
{
dp[j] = min(dp[j], dp[j - i * i] + 1);
}
}
*/
return dp[n];
}
};
Leetcode 518. Coin Change II
dp[j](考虑coins[i]的组合总和)就是所有的dp[j - coins[i]](不考虑coins[i]相加
本题要求凑成总和的组合数,不要求顺序:
需要先外层循环物品,内层遍历背包
如果先遍历背包再遍历物品 => 考虑进排列
for (int j = 0; j <= amount; j ++)
{
for (int i = 0; i < coins.size(); i ++)
{
if (j - coins[i] >= 0) dp[j] += dp[j - coins[i]];
}
}
会包含 {1, 5} 和 {5, 1}
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]] + dp[j];
}
}
return dp[amount];
}
};
Leetcode 377. Combination IV
排列!
注意顺序
\color{Red}注意顺序
注意顺序
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
vector<int> dp(target + 1, 0);
dp[0] = 1;
// combination
for (int i = 1; i <= target; i ++)
{
for (int j = 0; j < nums.size(); j ++)
{
if (i >= nums[j] && dp[i] < INT_MAX - dp[i - nums[j]]) dp[i] += dp[i - nums[j]];
}
}
return dp[target];
}
};
Leetcode 494. Target Sum
0/1 背包!(每个物品只用一次)+ 组合!
dp[j] += dp[j - nums[i]]
class Solution {
public:
int findTargetSumWays(vector<int>& nums, int target) {
// left + right = sum
// right = sum - left
// left - (sum - left) = target
// left = (target + sum) / 2
int sum = 0;
for (auto n: nums) sum += n;
if ((sum + target) % 2) return 0;
if (abs(target) > sum) return 0;
target = (sum + target) / 2;
//if (target < 0) return 0;
vector<int> dp(target + 1, 0);
dp[0] = 1;
for (int i = 0; i < nums.size(); i ++)
{
for (int j = target; j >= nums[i]; j --)
{
dp[j] += dp[j - nums[i]];
}
}
return dp[target];
}
};
Leetcode 474. Ones and Zeros
0/1背包
- 确定dp table以及下标含义:
dp[i][j] = 最多有 i 个0和 j 个1的strs的最大自己大小为dp[i][j] - 确定递推公式:
dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1)
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
// dp[i][j]: having maximum i of zero and j of 1's subset's size
// 0/1 take or not take
// go through object
for (int i = 0; i < strs.size(); i ++)
{
int oneNum = 0, zeroNum = 0;
for (auto c: strs[i])
{
if (c == '0') zeroNum ++;
else oneNum ++;
}
// go through pack volume from end to start
for (int i = m; i >= zeroNum; i --)
{
for (int j = n; j >= oneNum; j --)
{
dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
}
}
}
return dp[m][n];
}
};