作者:云小逸
个人主页:云小逸的主页
Github:云小逸的Github
motto:要敢于一个人默默的面对自己,强大自己才是核心。不要等到什么都没有了,才下定决心去做。种一颗树,最好的时间是十年前,其次就是现在!学会自己和解,与过去和解,努力爱自己。==希望春天来之前,我们一起面朝大海,春暖花开!==
专栏:C++ 专栏:Java语言专栏:Linux学习
专栏:C语言初阶专栏:数据结构专栏:备战蓝桥杯
今天我们接着上一篇博客继续学习背包问题:多重背包问题,这里将介绍完全背包问题的二维解法和一维解法以及优化版本,希望你可以喜欢。——————————————————————————————
多重背包问题是背包问题的一个变种。在这个问题中,每个物品有一个重量和一个价值,且可重复选择多次。背包有一个容量限制,问怎样选择物品可以使得背包中的总价值最大。
对于二维多重背包问题,我们可以将其转化为一维多重背包问题来解决。具体来说,我们可以将第 i i i 个物品拆成 s i s_i si 个物品,每个物品的重量和价值都是原来的 w i s i \frac{w_i}{s_i} siwi 和 v i s i \frac{v_i}{s_i} sivi。这样就将二维多重背包问题转化为了一维多重背包问题。
代码实现如下:
int dp[N];
for (int i = 1; i <= n; i++) {
for (int j = m; j >= w[i]; j--) {
for (int k = 1; k <= s[i] && k * w[i] <= j; k++) {
dp[j] = max(dp[j], dp[j - k * w[i]] + k * v[i]);
}
}
}
时间复杂度为 O ( n m ∑ i = 1 s v i ) O(nm\sum\limits_{i=1}^s v_i) O(nmi=1∑svi)。
对于一维多重背包问题,我们可以使用动态规划来解决。设 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示前 i i i 个物品中,容量为 j j j 的背包所能装下的最大价值。则有以下状态转移方程:
d p [ i ] [ j ] = max { d p [ i − 1 ] [ j − k ⋅ w i ] + k ⋅ v i } ( 0 ≤ k ≤ ⌊ j w i ⌋ ) dp[i][j] = \max\{dp[i-1][j-k\cdot w_i]+k\cdot v_i\} \quad (0\leq k\leq \lfloor\frac{j}{w_i}\rfloor) dp[i][j]=max{dp[i−1][j−k⋅wi]+k⋅vi}(0≤k≤⌊wij⌋)
其中 w i w_i wi 表示第 i i i 个物品的重量, v i v_i vi 表示第 i i i 个物品的价值。这个方程的意思是,我们可以选择第 i i i 个物品的 k k k 个,然后再加上前 i − 1 i-1 i−1 个物品在剩余容量 j − k ⋅ w i j-k\cdot w_i j−k⋅wi 的情况下所能取得的最大价值。
代码实现如下:
int dp[N];
for (int i = 1; i <= n; i++) {
for (int j = m; j >= w[i]; j--) {
for (int k = 1; k * w[i] <= j && k <= s[i]; k++) {
dp[j] = max(dp[j], dp[j - k * w[i]] + k * v[i]);
}
}
}
时间复杂度为 O ( n m ∑ i = 1 s v i ) O(nm\sum\limits_{i=1}^s v_i) O(nmi=1∑svi)。
以上两种方法的时间复杂度都比较高,无法通过本题。我们需要对其进行优化。
观察上面的状态转移方程,我们可以发现其中有一个 max \max max 函数,这个函数是比较耗时的。我们可以使用单调队列来优化这个 max \max max 函数。具体来说,我们可以将 d p [ i − 1 ] [ j − k ⋅ w i ] + k ⋅ v i dp[i-1][j-k\cdot w_i]+k\cdot v_i dp[i−1][j−k⋅wi]+k⋅vi 放入一个单调队列中,然后取队首元素即可。
代码实现如下:
int dp[N];
deque<int> q;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < w[i]; j++) {
q.clear();
for (int k = 0; k * w[i] + j <= m; k++) {
int x = k * w[i] + j;
if (!q.empty() && k - q.front() > s[i]) {
q.pop_front();
}
if (!q.empty()) {
dp[x] = max(dp[x], dp[x - w[i]] + k * v[i]);
while (!q.empty() && dp[x - w[i]] - q.back() * v[i] >= 0) {
q.pop_back();
}
}
q.push_back(k);
}
}
}
时间复杂度为 O ( n m log s ) O(nm\log s) O(nmlogs)。
十分感谢你可以耐着性子把它读完和我可以坚持写到这里,送几句话,对你,也对我:
1. 努力读书,尤其是家境普通或者家境差的,去学习,你的目标就是改变自己的命运,骆家辉三代人才进白宫,英国印度裔首相苏纳克,三代人才进白金汉宫,所以有一代人必须做出付出,没办法,你的最大任务就是完成原始资本积累。
2.不要被别人牵着鼻子走,不要别人说什么就信什么,学会分辨是非。
3.不要随便把情绪写在脸上,冷静不代表软弱,理智不等于认怂。
4.我们不能一边过着糟糕的生活,一边期待好事发生在我们身上。
5.穷不沾色,富不沾赌。
最后如果觉得我写的还不错,请不要忘记点赞✌,收藏✌,加关注✌哦(。・ω・。)
愿我们一起加油,奔向更美好的未来,愿我们从懵懵懂懂的一枚菜鸟逐渐成为大佬。加油,为自己点赞!