目录
一、基础01背包
即各物品数量只有一件,问装满价值最大是多少
1、二维 dp 数组
定义 dp[i][j] 为从下标 [0...i] 的物品中任选,放进容量为 j 的背包里,能获得的最大价值
int knapsack_01_1(vector<int>& weight, vector<int>& value, int capacity)
{
int n = weight.size();
vector<vector<int>> dp(n, vector<int>(capacity + 1));
for (int j = weight[0]; j <= capacity; j++)
{
dp[0][j] = value[0];
}
for (int i = 0; i < n; i++)
{
for (int j = weight[i]; j <= capacity; j++)
{
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
}
return dp[n - 1][capacity];
}
2、滚动数组优化为一维数组
定义 dp[j] 为容量为 j 的背包能获得的最大价值
int knapsack_01_2(vector<int>& weight, vector<int>& value, int capacity)
{
int n = weight.size();
vector<int> dp(capacity + 1);
for (int i = 0; i < n; i++)
{
for (int j = capacity; j >= weight[i]; j--)
{
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
return dp[capacity];
}
二、01背包应用
1、求装满后的最大价值变形
原题力扣1049 最后一块石头的重量II . - 力扣(LeetCode)
int lastStoneWeightII(vector<int>& stones)
{
int n = stones.size();
int sum = 0;
for (const auto& x : stones)
{
sum += x;
}
int target = sum / 2;
vector<int> dp(target + 1, 0);
for (int j = stones[0]; j <= target; j++)
{
dp[j] = stones[0];
}
for (int i = 1; i < n; i++)
{
for (int j = target; j >= stones[i]; j--)
{
dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
}
}
int rest = sum - dp[target];
return rest - dp[target];
}
2、求能不能装满
原题力扣416 分割等和子集 . - 力扣(LeetCode)
bool canPartition(vector<int>& nums)
{
int n = nums.size();
int sum = 0;
for (const auto& x : nums)
{
sum += x;
}
if (sum % 2 != 0)
{
return false;
}
int target = sum / 2;
vector<int> dp(target + 1, 0);
for (int i = 0; i < nums.size(); i++)
{
for (int j = target; j >= nums[i]; j--)
{
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
}
}
return dp[target] == target;
}
3、求装满有几种办法
力扣原题494 目标和 . - 力扣(LeetCode)
int findTargetSumWays(vector<int>& nums, int target)
{
int n = nums.size();
int sum = 0;
for (const auto& x : nums)
{
sum += x;
}
int positive = (sum + target) / 2;
if ((sum + target) % 2 != 0)
{
return 0;
}
if (abs(target) > sum)
{
return 0;
}
vector<int> dp(positive + 1);
dp[0] = 1;
for (int i = 0; i < n; i++)
{
for (int j = positive; j >= nums[i]; j--)
{
dp[j] += dp[j - nums[i]];
}
}
return dp[positive];
}
4、求装满后背包有多大(二维背包)
力扣原题474 一和零 . - 力扣(LeetCode)
int findMaxForm(vector<string>& strs, int m, int n)
{
vector<vector<int>> dp(m + 1, vector<int>(n + 1));
dp[0][0] = 0;
for (const auto& str : strs)
{
int zero = 0, one = 0;
for (const auto& ch : str)
{
if (ch == '0')
{
zero++;
}
else
{
one++;
}
}
for (int i = m; i >= zero; i--)
{
for (int j = n; j >= one; j--)
{
dp[i][j] = max(dp[i][j], dp[i - zero][j - one] + 1);
}
}
}
return dp[m][n];
}
三、基础完全背包
即各物品数量不限,问装满价值最大是多少
int knapsack_complete(vector<int>& weight, vector<int>& value, int capacity)
{
int n = weight.size();
vector<int> dp(capacity + 1, 0);
for (int i = 0; i < weight.size(); i++)
{
for (int j = weight[i]; j <= capacity; j++)
{
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
return dp[capacity];
}
四、完全背包应用
1、求装满有几种办法(组合数)
原题力扣518 零钱兑换II . - 力扣(LeetCode)
int change(int amount, vector<int>& coins)
{
int n = coins.size();
vector<int> dp(amount + 1);
dp[0] = 1;
for (int i = 0; i < n; i++) // 遍历物品
{
for (int j = coins[i]; j <= amount; j++) // 遍历背包
{
dp[j] += dp[j - coins[i]];
}
}
return dp[amount];
}
2、求装满有几种办法(排列数)
原题力扣377 组合总和IV . - 力扣(LeetCode)
int combinationSum4(vector<int>& nums, int target)
{
vector<int> dp(target + 1);
dp[0] = 1;
for (int i = 0; i <= target; i++) // 遍历背包
{
for (int j = 0; j < nums.size(); j++) // 遍历物品
{
if (i >= nums[j])
{
dp[i] += dp[i - nums[j]];
}
}
}
return dp[target];
}
3、求装满时最少用多少物品
原题力扣279 完全平方数 . - 力扣(LeetCode)
int numSquares(int n)
{
vector<int> dp(n + 1, INT_MAX);
dp[0] = 0;
for (int i = 1; i <= n; i++)
{
for (int j = i * i; j <= n; j++)
{
dp[j] = min(dp[j], dp[j - i * i] + 1);
}
}
return dp[n];
}
原题力扣322 零钱兑换 . - 力扣(LeetCode)
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], dp[j - coins[i]] + 1);
}
}
}
if (dp[amount] == INT_MAX)
{
return -1;
}
return dp[amount];
}
4、求能不能装满背包(排列数)
原题力扣139 单词拆分 . - 力扣(LeetCode)
bool wordBreak(string s, vector<string>& wordDict)
{
int n = s.size();
unordered_set<string> st(wordDict.begin(), wordDict.end());
vector<bool> dp(n + 1);
dp[0] = true;
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < i; j++)
{
if (st.find(s.substr(j, i - j)) != st.end() &&
dp[j] == true)
{
dp[i] = true;
}
}
}
return dp[n];
}