背包问题
背包问题是DP,也是贪心问题。下面进行举例,我们假设背包的容量为8,有4件物品,编号分别为1,2,3,4,体积分别为2,3,4,5,价值分别为3,4,5,6 。问装入价值最大为多少(初级)以及是那几件物品(进阶),当然每件物品放一次。
先来分析解决第一个问题,装入价值最大为多少,使用DP。先列个表,然后分析得出状态转移方程。如下表所示,为什么只有四件物品却要有五个编号。当什么都没放的时候,我们记为编号0。当然不管背包容量多少,不放东西时价值都为0.
从编号1的物品开始分析,没有容量或者容量不够时,价值为0,当容量大于等于2时,只考虑物品1的时候,价值均为3;接下来分析物品2,容量0~2显然存不下物品2的,价值等于上一行的值,容量3,考虑物品2放还是不放,进行比较,显然放的时候价值最大;容量4的时候物品1放不下,做法和3一样;容量为5的时候,显然放下1,2物品时价值最大,下面以此类推
根据上述表格的分析,我们可以得出状态转移方程。假设物品体积我们用数组c[]表示,价值用数组val[]表示。
1.状态转移方程:f[i][j] = f[i-1][j] (当前物品装不下时)
f[i][j] = max(f[i-1][j], val[i] + f[i][j-c[i]]) (当前物品可以装时,第二个表达式为当前容量去除当前物品的容量的最大值)
2.选用二维数组作为数据结构
3.初始化,将第一行初始化为0
第二个问题分析:我们在第一个问题的基础上进行分析,已经找到了最大的价值,那么比对一下上一次加的是哪个情况下的值(上述两种情况),去确定是几号物品,使用回溯法。
结束条件:i=0,遍历到没有物品时结束
all station:考虑两种情况的比对,然后递归下一次
推荐B站视频学习:https://www.bilibili.com/video/BV1K4411X766?from=search&seid=3866792667009135377
参考代码:(第一个问题)
class Solution{
public:
vector<int> object;
int b_b(vector<int>& c, vector<int>& val, int capacity)
{
//DP
vector<vector<int>> f(c.size(), vector<int>(capacity + 1,0));
//填表
for (int i = 1; i < c.size(); i++)
{
for (int j = 1; j <= capacity; j++)
{
if (c[i] <= j)
{
f[i][j] = max(f[i-1][j],f[i-1][j-c[i]]+val[i]);
}
else{
f[i][j] = f[i-1][j];
}
}
}
find_object(f,val,c,4,8);
return f[c.size()-1][capacity];
}
void find_object(vector<vector<int>>& dp, vector<int>& val, vector<int>& c, int i, int j)
{
if (i == 0)
{
return;//找完了
}
if (dp[i][j] == dp[i - 1][j])
{
object[i] = 0;
find_object(dp, val, c, i - 1, j);
}
else if (dp[i][j] == dp[i - 1][j - c[i]] + val[i])
{
object[i] = 1;
find_object(dp, val, c, i - 1, j-c[i]);
}
}
};
int main()
{
Solution solution;
solution.object.resize(5);
vector<int> c;
vector<int> val;
for (int i = 0; i <= 4; i++)
{
if (i == 0)
{
c.push_back(i);
val.push_back(i);
}
else{
c.push_back(i+1);
val.push_back(i+2);
}
}
cout << solution.b_b(c,val,8) << endl;
system("pause");
return 0;
}