01背包问题(二维数组)
https://mp.weixin.qq.com/s/FwIiPPmR18_AJO5eiidT6w
/*
再回顾一下dp[i][j]的含义:从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
那么可以有两个方向推出来dp[i][j],
不放物品i:由dp[i - 1][j]推出,即背包容量为j,里面不放物品i的最大价值,此时dp[i][j]就是dp[i - 1][j]。(其实就是当物品i的重量大于背包j的重量时,物品i无法放进背包中,所以被背包内的价值依然和前面相同。)
放物品i:由dp[i - 1][j - weight[i]]推出,dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值,那么dp[i - 1][j - weight[i]] + value[i] (物品i的价值),就是背包放物品i得到的最大价值
所以递归公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
*/
void test_2_wei_bag_problem1()
{
vector<int> weight = {1, 3, 4};
vector<int> value = {15, 20, 30};
int bagWeight = 4;
// 二维数组
vector<vector<int>> dp(weight.size() + 1, vector<int>(bagWeight + 1, 0));
// 初始化
for (int j = bagWeight; j >= weight[0]; j--) {
dp[0][j] = dp[0][j - weight[0]] + value[0];
}
// weight数组的大小 就是物品个数
for(int i = 1; i < weight.size(); i++) { // 遍历物品
for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量
if (j < weight[i]) dp[i][j] = dp[i - 1][j];
else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
}
cout << dp[weight.size() - 1][bagWeight] << endl;
}
int main() {
test_2_wei_bag_problem1();
}
01背包问题(一维滚动数组)
https://mp.weixin.qq.com/s/M4uHxNVKRKm5HPjkNZBnFA
void test_1_wei_bag_problem()
{
vector<int> weight = {1, 3, 4};
vector<int> value = {15, 20, 30};
int bagWeight = 4;
// 初始化
vector<int> dp(bagWeight + 1, 0);
for(int i = 0; i < weight.size(); i++) { // 遍历物品
for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
cout << dp[bagWeight] << endl;
}
int main()
{
test_1_wei_bag_problem();
}
0-1背包应用
https://leetcode-cn.com/problems/ones-and-zeroes/
题目描述:
给你一个二进制字符串数组 strs 和两个整数 m 和 n 。
请你找出并返回 strs 的最大子集的大小,该子集中 最多 有 m 个 0 和 n 个 1 。
如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。
示例 1:
输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
输出:4
解释:最多有 5 个 0 和 3 个 1 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。
其他满足题意但较小的子集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不满足题意,因为它含 4 个 1 ,大于 n 的值 3 。
示例 2:
输入:strs = ["10", "0", "1"], m = 1, n = 1
输出:2
解释:最大的子集是 {"0", "1"} ,所以答案是 2 。
提示:
1 <= strs.length <= 600
1 <= strs[i].length <= 100
strs[i] 仅由 '0' 和 '1' 组成
1 <= m, n <= 100
代码实现:
class Solution
{
public:
// 01背包问题
// dp[i][j][k] 从下标为[0-i]的物品里任意取,放进容量为j个0, k个1 背包中 子集最大的值
int findMaxForm(vector<string> &strs, int m, int n)
{
int size = strs.size(); // 物品, string 中的 0 对应背包容量 m , 1 对应背包容量 n
// m , n 是背包容量
vector< vector<vector<int>> > dp(size + 1, vector<vector<int>>( m + 1, vector<int>(n + 1, 0)));
// 初始化
// dp[0][j][k] = 0
//
//
// 递推公式:
// 1.不放物品 i : dp[i-1][j][k]
// 2.放物品 i : dp[i-1][j - zeros][k - ones] + 1
// dp[i][j][k] = max( dp[i-1][j][k] , dp[i - 1][j - zeros][k - ones] + 1 );
//
// 实现时,因 dp[0][j][k] 需要初始化为0,那么 dp[i][j][k] 对应的物品是 strs[i - 1], 所以, i要从1开始
for(int i = 1; i < size + 1; i++ )
{
int zeros = 0; // 字符串中 '0' 的个数
int ones = 0; // 字符串中 '1' 的个数
for_each(strs[i - 1].begin(), strs[i - 1].end(), [&zeros, &ones](char ch){
if('0' == ch) zeros++;
else ones++;
});
for(int j = 0; j <= m; j++)
{
for(int k = 0; k <= n; k++)
{
dp[i][j][k] = dp[i-1][j][k];
if( j - zeros >= 0 && k - ones >= 0) {
dp[i][j][k] = max( dp[i-1][j][k] , dp[i - 1][j - zeros][k - ones] + 1 );
}
}
}
}
return dp[size][m][n];
}
};
void test(vector<string> strs, int m, int n, int expected)
{
Solution sol;
int result = sol.findMaxForm(strs, m, n);
if(result != expected)
{
cout << "FAILED" << " result " << result << " expected " << expected << endl;
return;
}
cout << "PASSED" << endl;
}
int main()
{
test({"10", "0", "1"}, 1, 1, 2);
test({"10", "0001", "111001", "1", "0"}, 5, 3, 4);
test({"111","1000","1000","1000"}, 9, 3, 3);
test({"011111","001","001"}, 4, 5, 2);
test({"11111","100","1101","1101","11000"}, 5, 7, 3);
test({"0000111","0001","0000001111111","00011111","000011111","00000011","0111111","0000000001111111","0011","001111","00000001111","0011","0000111111111","0001111111","011111111"}, 4, 6, 2);
return 0;
}
进阶:优化时间和空间复杂度