欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 资讯 > 算法【多重背包】

算法【多重背包】

2025/2/3 13:48:30 来源:https://blog.csdn.net/W_O_Z/article/details/145371895  浏览:    关键词:算法【多重背包】

多重背包是指每一种物品给定数量的限制,进行可能性展开。对于多重背包的枚举优化,本文介绍最常用的二进制分组优化,其他优化如单调队列优化可自行学习。

下面通过题目加深理解。

题目一

测试链接:宝物筛选 - 洛谷

分析:这道题就是典型的多重背包模板,对于多重背包可能性的展开也十分容易想到,对于第i个物品不取或者依次取1个、2个、3个等。这样就可以写出记忆化搜索的版本,代码如下。

#include <iostream>
using namespace std;
int n, W;
int treasure[100][3];
int dp[100][40001];
int f(int index, int weight){if(index == n){return 0;}if(dp[index][weight] != -1){return dp[index][weight];}int ans = f(index+1, weight);for(int i = 1;i <= treasure[index][2] && weight - i * treasure[index][1] >= 0;++i){ans = ans > f(index+1, weight - i * treasure[index][1]) + i * treasure[index][0] ?ans : f(index+1, weight - i * treasure[index][1]) + i * treasure[index][0];}dp[index][weight] = ans;return ans;
}
int main(void){scanf("%d%d", &n, &W);for(int i = 0;i < n;++i){scanf("%d%d%d", &treasure[i][0], &treasure[i][1], &treasure[i][2]);}for(int i = 0;i < n;++i){for(int j = 0;j <= W;++j){dp[i][j] = -1;}}printf("%d", f(0, W));return 0;
}

其中,f方法返回在从下标为index物品开始取,剩余重量为weight的情况下取得的最大值。不过这道题目对于时间复杂度要求比较高,记忆化搜索的版本过不去。所以可以写出严格位置依赖的版本。代码如下。

#include <iostream>
using namespace std;
int n, W;
int treasure[100][3];
int dp[101][40001];
int main(void){scanf("%d%d", &n, &W);for(int i = 0;i < n;++i){scanf("%d%d%d", &treasure[i][0], &treasure[i][1], &treasure[i][2]);}for(int i = 0;i <= W;++i){dp[n][i] = 0;}for(int i = 0;i < n;++i){dp[i][0] = 0;}for(int i = n-1;i >= 0;--i){for(int j = 0;j <= W;++j){dp[i][j] = dp[i+1][j];for(int k = 1;k <= treasure[i][2] && j - k * treasure[i][1] >= 0;++k){dp[i][j] = dp[i][j] > dp[i+1][j-k*treasure[i][1]] + k * treasure[i][0] ?dp[i][j] : dp[i+1][j-k*treasure[i][1]] + k * treasure[i][0];}}}printf("%d", dp[0][W]);return 0;
}

其中,dp数组的含义和记忆化搜索中f方法的含义基本相同,因为严格位置依赖的版本就是根据记忆化搜索写出来的。不过,严格位置依赖的版本依旧过不去。下面还可以写出空间压缩的版本。代码如下。

#include <iostream>
using namespace std;
int n, W;
int treasure[100][3];
int dp[40001];
int main(void){scanf("%d%d", &n, &W);for(int i = 0;i < n;++i){scanf("%d%d%d", &treasure[i][0], &treasure[i][1], &treasure[i][2]);}for(int i = 0;i <= W;++i){dp[i] = 0;}for(int i = n-1;i >= 0;--i){for(int j = W;j >= 0;--j){for(int k = 1;k <= treasure[i][2] && j - k * treasure[i][1] >= 0;++k){dp[j] = dp[j] > dp[j-k*treasure[i][1]] + k * treasure[i][0] ?dp[j] : dp[j-k*treasure[i][1]] + k * treasure[i][0];}}}printf("%d", dp[W]);return 0;
}

虽然空间压缩相对于严格位置依赖不会有明显的时间复杂度优化,不过对于这道题目空间压缩还是压着线过了。对于多重背包的真正的优化在题目二体现。

题目二

测试链接:宝物筛选 - 洛谷

分析:对于多重背包一般的优化是将多重背包转化为01背包进行求解。就是将多重背包的件数转化为多个二进制背包的形式,这样可以通过转化出的多个物品不同的取舍方案得到对多重背包取不同件数的体现。比如件数为7,可以从1开始,转化为1,2,4三个背包,对于多重背包7件取多少件,可以通过1,2,4这三个背包取或不取得到。代码如下。

#include <iostream>
using namespace std;
int n, W;
int treasure[2000][2];
int dp[40001];
int main(void){int v_i, w_i, m_i, index = 0, temp;scanf("%d%d", &n, &W);for(int i = 0;i < n;++i){scanf("%d%d%d", &v_i, &w_i, &m_i);temp = 1;while (m_i >= temp){treasure[index][0] = temp * v_i;treasure[index++][1] = temp * w_i;m_i -= temp;temp *= 2;}if(m_i != 0){treasure[index][0] = m_i * v_i;treasure[index++][1] = m_i * w_i;}}for(int i = 0;i <= W;++i){dp[i] = 0;}for(int i = 0;i < index;++i){for(int j = W;j >= 0 && j - treasure[i][1] >= 0;--j){dp[j] = dp[j] > dp[j-treasure[i][1]] + treasure[i][0] ?dp[j] : dp[j-treasure[i][1]] + treasure[i][0];}}printf("%d", dp[W]);return 0;
}

题目三

测试链接:樱花 - 洛谷

分析:这道题看似有一个完全背包,但是可以将完全背包转化为多重背包。即如果次数表示无数次,但是要在满足时间条件的情况下,所以求出一个最大次数和原无数次是等价的,这样就转化为了多重背包。代码如下。

#include <iostream>
using namespace std;
int Time, n;
int tree[1000000][2];
int dp[1001];
int main(void){int hour1, hour2, minute1, minute2;char ch;int T_i, C_i, P_i, temp, index = 0;cin>>hour1>>ch>>minute1>>hour2>>ch>>minute2;Time = (hour2 - hour1) * 60 + minute2 - minute1;cin>>n;for(int i = 0;i < n;++i){scanf("%d%d%d", &T_i, &C_i, &P_i);temp = 1;if(P_i == 0){P_i = Time / T_i;}while (P_i >= temp){tree[index][0] = temp * T_i;tree[index++][1] = temp * C_i;P_i -= temp;temp *= 2;}if(P_i > 0){tree[index][0] = P_i * T_i;tree[index++][1] = P_i * C_i;}}for(int i = 0;i <= Time;++i){dp[i] = 0;}for(int i = 0;i < index;++i){for(int j = Time;j >= 0 && j - tree[i][0] >= 0;--j){dp[j] = dp[j] > dp[j-tree[i][0]] + tree[i][1] ?dp[j] : dp[j-tree[i][0]] + tree[i][1];}}printf("%d", dp[Time]);return 0;
}

其中,dp数组表示在下标0~i的物品取,剩余重量为j的情况下取得的最大值。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com