【动态规划 背包问题】
416. 分割等和子集(背包)
class Solution {
public:
// dp[i][j] 能获得的最大价值:
// i --> 代表前i件商品
// j --> 能得到的重量j
// 0/1背包状态转移方程:
// dp[i][j] = max(dp[i-1][j-w[i]] + v[i], dp[i-1][j]);
bool canPartition(vector<int>& nums) {
int sum = [](vector<int> &n){
int num = 0;
for(auto i: n)
num += i;
return num;
}(nums);
if(sum % 2)
return false;
int target = sum / 2;
int len = nums.size();
int dp[len][target+1];
for(int j=0;j<=target;++j){
if(j >= nums[0])
dp[0][j] = nums[0];
else
dp[0][j] = 0;
if(dp[0][j] == target)
return true;
}
for(int i=1;i<len;++i){
int v = nums[i];
int w = nums[i];
dp[i][0] = 0;
for(int j=1;j<=target;++j){
if(j < w)
dp[i][j] = dp[i-1][j];
else
dp[i][j] = max(dp[i-1][j-w] + v, dp[i-1][j]);
if(dp[i][j] == target)
return true;
}
}
return false;
}
};
进行空间优化
class Solution {
public:
// dp[i][j] 能获得的最大价值:
// i --> 代表前i件商品
// j --> 能得到的重量j
// 0/1背包状态转移方程:
// dp[i][j] = max(dp[i-1][j-w[i]] + v[i], dp[i-1][j]);
bool canPartition(vector<int>& nums) {
int sum = [](vector<int> &n){
int num = 0;
for(auto i: n)
num += i;
return num;
}(nums);
if(sum % 2)
return false;
int target = sum / 2;
int len = nums.size();
int dp[2][target+1];
for(int j=0;j<=target;++j){
if(j >= nums[0])
dp[0][j] = nums[0];
else
dp[0][j] = 0;
if(dp[0][j] == target)
return true;
}
for(int i=1;i<len;++i){
int cur = i % 2;
int last = (i + 1) % 2;
int v = nums[i];
int w = nums[i];
dp[cur][0] = 0;
for(int j=1;j<=target;++j){
if(j < w)
dp[cur][j] = dp[last][j];
else
dp[cur][j] = max(dp[last][j-w] + v, dp[last][j]);
if(dp[cur][j] == target)
return true;
}
}
return false;
}
};
474. 一和零(多维费用背包)
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
vector<pair<int, int> > nums;
for_each(begin(strs), end(strs), [&nums](auto str){
int i = 0, j = 0;
for(char ch : str)
if(ch == '0') i++;
else j++;
nums.push_back(make_pair(i, j));
});
int dp[m+1][n+1];
memset(dp , 0 , sizeof(dp));
int result = 1;
dp[0][0] = 1;
for(auto num : nums){
int v = num.first;
int w = num.second;
for(int i=m; i>=v; --i)
for(int j=n; j>=w; --j){
dp[i][j] = max(dp[i][j], (dp[i-v][j-w] ? dp[i-v][j-w] + 1 : 0));
// cout << dp[i][j] << " " << i << " " << j << endl;
result = max(dp[i][j], result);
}
}
return result - 1;
}
};
322. 零钱兑换(完全背包)
硬币可以重复使用。
完全背包问题。完全背包只需要将 0-1 背包的逆序遍历 dp 数组改为正序遍历即可。
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
int dp[amount + 1];
int len = coins.size();
memset(dp, 0 , sizeof(dp));
dp[0] = 1;
for(auto coin: coins)
for(int i = coin; i<=amount; ++i)
dp[i] = dp[i-coin] ? (dp[i] ? min(dp[i-coin] + 1, dp[i]): dp[i-coin] + 1) : dp[i];
// cout << dp[i] << " " << i << " " << coin << endl;
return dp[amount] - 1;
}
};
518. 零钱兑换 II(完全背包)
请你计算并返回可以凑成总金额的硬币组合数
class Solution {
public:
int change(int amount, vector<int>& coins) {
int dp[amount+1];
memset(dp, 0 ,sizeof(dp));
dp[0] = 1;
for(auto coin : coins){
for(int i=coin; i<=amount; ++i){
dp[i] += dp[i-coin];
// cout << dp[i] << " " << i << " " << coin << endl;
}
}
return dp[amount];
}
};
139. 单词拆分(完全背包)
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
int s_len = s.size();
int len, w_len = wordDict.size();
int dp[s_len + 1];
memset(dp, 0 , sizeof(dp));
dp[0] = 1;
for(int i=0;i<s_len;++i){
for(auto &word : wordDict){
len = word.size();
if(i + 1 < len)
continue;
if(word == s.substr(i+1-len, len))
dp[i+1] |= dp[i+1-len];
}
}
return dp[s_len];
}
};
377. 组合总和 Ⅳ(完全背包)
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
int dp[target+1];
sort(nums.begin(), nums.end());
memset(dp, 0 , sizeof(dp));
dp[0] = 1;
for(int i=1;i<=target;++i)
for(int num : nums){
if(i < num || dp[i] >= INT_MAX - dp[i-num]) // 要注意一些奇奇怪怪的边界条件!!
continue;
dp[i] += dp[i-num];
}
return dp[target];
}
};