先推荐个视频
一、类型1,重量限制(每个仅取一次)求最大价值|01背包
题目:
给定N个物品,每个物品有一个重量W和一个价值V.你有一个能装M重量的背包.问怎么装使得所装价值最大.每个物品只有一个.
输入:
输入的第一行包含两个整数n, m,分别表示物品的个数和背包能装重量。
以后N行每行两个数Wi和Vi,表示物品的重量和价值
其中数据规模和约定:1<=N<=200,M<=5000.
输出:
输出1行,包含一个整数,表示最大价值。
思路1二维数组
思路:
动态规划问题
打表
找动态转换方程
dp[i][j]表示背包容量为j,第i个物品时的最大价值
i表示从物品1到物品i;j表示背包容量为j
我们可以从第一个物品开始,求出j=1到10时的最大价值
再找第二个物品,从j=1到10时的最大价值
再找第三个物品......
dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i])
2、二维数组代码:
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
int n, m;
int weight[210], value[210];
int dp[210][5010];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> weight[i] >> value[i];
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (weight[i] <= j) {
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
else
dp[i][j] = dp[i - 1][j];
}
}
cout << dp[n][m];
return 0;
}
思路2一维数组
思路
从上面的表可以看出,第i个物品时,求最大价值只与i-1个物品时最大价值有关,所以可以用一维数组dp[j]来表示背包容量为j时的最大价值。
dp[j]=max(dp[j],dp[j-weight[i]]+value[i])
之所以倒推是因为,递推关系是用i-1来推出的i,所以求i时,i-1还在。
如果正推,那就是用新的i推出新的i,就变成了多次取第i个物品
正推图:
代码
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
int n, m;
int weight[210], value[210];
int dp[5010];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> weight[i] >> value[i];
}
for (int i = 1; i <= n; i++) {
for (int j = m; j >=weight[i]; j--) {
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
cout << dp[m];
return 0;
}
二、类型2,重量和体积限制(每个仅取一次)求最大价值
题目:
有5件物品,它们分别有价值,体积和重量。
为了把它们装进一个背包里,其体积和重量必须符合要求,不超过背包要求的容量。
请问背包内装物品,其最大价值是多少。
输入:
第一行是两个数,V,W(V,W<=10000),表示背包可负载的最大体积和重量
输入包括5行。
每行都包含三个数字,pi,vi,wi,(pi,vi,wi<=10000)表示价值,体积和重量。
输出:
包括一个数,表示可装下的最大价值。
思路:
数据较少,可直接枚举
时间复杂度2^5
每件物品只有两种状态,0(不拿)、1(拿)
代码:
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
int v, w,pr,vo,we,mx;
int price[6], volume[6],weight[6];
int dp[6];
void func(int u) {
if (u <= 5) {
dp[u] = 0;
func(u + 1);
dp[u] = 1;
func(u + 1);
}
else {
pr = 0; vo = 0; we = 0;
for (int i = 1; i <= 5; i++) {
pr += price[i] * dp[i];
vo += volume[i] * dp[i];
we += weight[i] * dp[i];
}
if (pr > mx && vo <= v && we <= w) {
mx = pr;
}
}
}
int main() {
cin >> v >> w;
for (int i = 1; i <= 5; i++) {
cin >> price[i] >> volume[i] >> weight[i];
}
func(1);
cout << mx << endl;
return 0;
}
三、类型3,重量限制(每个取有限次)求最大价值|多重背包
题目
有n种物品,小王有一个能装m千克的背包,想要装点物品回去。
每种物品,有自己的重量w(千克)和价值v(元),以及他们的数量c。
现在,物品的数量很大,而种类也不少。
请你计算出,背包装的最大价值。
输入:
第一行是整数n(n<=100),m(m<=3000),表示物品的种类和背包容量。
接下来n行,每行3个数,w(w<=100),v(v<=100),c(c<=1000),表示重量,价值,和数量.
输出:
一个数,表示最大价值。
思路1二维数组
1、思路
//虽然物品数量有限制,但也可以分为取这个和不取这个物品两种情况
//数量方面的限制既可以纵向(看作是物品种类增加)也可以横向(看作是物品重量、价值的等比膨胀)看待
//在此是看作物品重量、价值的等比膨胀
在普通01背包的循环中加一个数量限制即可
2、代码
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
int n, m;
int weight[120], value[120], countt[120];
int dp[120][3010];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> weight[i] >> value[i] >> countt[i];
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
//虽然物品数量有限制,但也可以分为取这个和不取这个物品两种情况
//数量方面的限制既可以纵向(看作是物品种类增加)也可以横向(看作是物品重量、价值的等比膨胀)看待
//在此是看作物品重量、价值的等比膨胀
dp[i][j] = dp[i - 1][j];//不取这个物品
for (int k = 1; k <= countt[i]; k++) {//取这个物品
if (j >= weight[i]*k) {
dp[i][j] = max(dp[i][j], dp[i-1][j - weight[i]*k] + value[i]*k);//这个物品膨胀到几倍的时候最合适
}
}
}
}
cout << dp[n][m];
return 0;
}
思路2一维数组
思路
和思路1一样,但优化了数组,
2、代码
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
int n, m;
int weight[120], value[120], countt[120];
int dp[3010];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> weight[i] >> value[i] >> countt[i];
}
for (int i = 1; i <= n; i++) {
for (int j = m; j >= 1; j--) {
for (int k = 1; k <= countt[i] && j >= weight[i] * k; k++) {
dp[j] = max(dp[j], dp[j - weight[i] * k] + value[i] * k);
}//此处dp[j - weight[i] * k]指更新前的数据
}
}
cout << dp[m];
return 0;
}