物品要么放,要么不放,即每种物品最多可以放一个,求最大收益。
/*
输入:
n 物品个数
m 背包容量
w[0] v[0] 重量,价值
w[1] v[1] 重量,价值
*
w[n-1] v[n-1] 重量,价值
输出:
dp[m] 最大价值
*/
/*
输入1:
3
10
2 4
4 7
7 6
输入2:
3
10
2 7
4 5
7 6
*/
#include
#include
using namespace std;
int main()
{
//输入
int n; //物品个数
int m; //背包容量
cin>>n>>m
vector w(n); //重量
vector v(n); //价值
for(int i=0; i>w[i]>>v[i];
}
//dp,N*M
vector dp(m+1, 0); //一维数组,相当于第-1行(0个物品/没有物品)为全为0,因此不需要另外初始化
for(int i=0; i=w[i]; j--)
{
dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
}
}
//输出
cout << dp[m] << endl; //输出结果
return 0;
}
每种物品个数不限,求最大收益。
/*
完全背包:每种物品个数不限
输入:
n 物品个数
m 背包容量
w[0] v[0] 重量,价值
w[1] v[1] 重量,价值
*
w[n-1] v[n-1] 重量,价值
输出:
dp[m] 最大价值
*/
/*
输入1:
3
10
2 4
4 7
7 6
输出:
20
输入2:
3
10
2 7
4 5
7 6
输出:
35
输入3:
5
10
5 1
4 2
3 3
2 4
1 5
输出:
50
*/
#include
#include
using namespace std;
int main()
{
//输入
int n; //物品类数
int m; //背包容量
cin>>n>>m;
vector w(n); //重量
vector v(n); //价值
for(int i=0; i>w[i]>>v[i];
}
//dp
/* 方式一,N*K*M
//判断一次多少件物品能够获得最大收益
vector dp(m+1, 0); //一维数组,相当于第-1行(0个物品/没有物品)为全为0,因此不需要另外初始化
for(int i=0; i=w[i]; j--)
{
for(int k=1; k*w[i]<=j; k++) //多少件物品能够获得最大值
{
dp[j] = max(dp[j], dp[j-k*w[i]] + k*v[i]);
}
}
}
//*/
/* 方式二,N*K*M
//重复 K=m/w[i] 次 01背包
vector dp(m+1, 0); //一维数组,相当于第-1行(0个物品/没有物品)为全为0,因此不需要另外初始化
for(int i=0; i=w[i]; j--)
{
dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
}
}
}
//*/
/* 方式三,N*K*M
//计算每种物品数量上限,扩充物品数量
int nn = n;
for(int i=0; i dp(m+1, 0); //一维数组,相当于第-1行(0个物品/没有物品)为全为0,因此不需要另外初始化
for(int i=0; i=w[i]; j--)
{
dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
}
}
//*/
//* 方式四,N*M
//至少放一个,比较当前行,非前一行
vector dp(m+1, 0); //把0个物品放入背包,获得的价值为0
for(int i=0; i
每种物品个数有限,求最大收益。
/*
多重背包:每种物品个数有限
输入:
n 物品个数
m 背包容量
w[0] v[0] c[0] 重量,价值,数量
w[1] v[1] c[1] 重量,价值,数量
*
w[n-1] v[n-1] c[n-1] 重量,价值,数量
输出:
dp[m] 最大价值
*/
/*
输入1:
3
10
2 4 2
4 7 3
7 6 4
输出:
18
输入2:
3
10
2 7 3
4 5 2
7 6 1
输出:
26
输入3:
5
10
5 1 1
4 2 2
3 3 1
2 4 2
1 5 1
输出:
16
*/
#include
#include
using namespace std;
int main()
{
//输入
int n; //物品类数
int m; //背包容量
cin>>n>>m;
vector w(n); //重量
vector v(n); //价值
vector c(n); //数量
for(int i=0; i>w[i]>>v[i]>>c[i];
}
//dp
/* 方式一,N*K*M
//判断一次多少件物品能够获得最大收益
vector dp(m+1, 0); //一维数组,相当于第-1行(0个物品/没有物品)为全为0,因此不需要另外初始化
for(int i=0; i=w[i]; j--)
{
for(int k=1; k*w[i]<=j && k<=c[i]; k++) //当前容量下,多少件本物品能够获得最大值
{
dp[j] = max(dp[j], dp[j-k*w[i]] + k*v[i]);
}
}
}
//*/
/* 方式二,N*K*M
//每件物品进行 K 次01背包
vector dp(m+1, 0); //一维数组,相当于第-1行(0个物品/没有物品)为全为0,因此不需要另外初始化
for(int i=0; i=w[i]; j--)
{
dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
}
}
}
//*/
/* 方式三,N*K*M,K是平均每个物品限制
//计算每种物品数量上限,扩充物品数量,增加了内存,不如方式二
int nn = n;
for(int i=0; i dp(m+1, 0); //一维数组,相当于第-1行(0个物品/没有物品)为全为0,因此不需要另外初始化
for(int i=0; i=w[i]; j--)
{
dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
}
}
//*/
//* 方式四,N*log(K)*M
//在方式二基础上,二进制简化,减少了k层的判断次数。
//把多个本种物品看成一个物品,体积、价值加倍,但是组合起来同样可以达到在限制范围内的任意数量。
vector dp(m+1, 0);
for(int i=0; i= m)
{
for(int j=w[i]; j<=m; j++)
{
dp[j] = max(dp[j], dp[j-w[i]]+v[i]);
}
continue;
}
//否则,是多重背包,简化写法
int number = c[i]; //物品数限制,剩余物品数
for(int k=1; k<=number ; k*=2) //放入 1,2,4,...,kk 个“本种物品”,且 sum = 1+2+4+...+kk <= number,然后判断剩下的 rest = number-sum 件物品
{
for(int j=m; j>=k*w[i]; j--)
{
dp[j] = max(dp[j], dp[j-k*w[i]] + k*v[i]);
}
number -= k; //剩余物品数,判断能否继续取k个本种物品
}
for(int j=m; j>=number*w[i]; j--)
{
dp[j] = max(dp[j], dp[j-number*w[i]] + number*v[i]);
}
}
//*/
//输出
cout << dp[m] << endl;
return 0;
}
每种物品个数可以是1个,也可以是K个,也可以是无限个(用0表示)。
题解使用了方式二,K次01的思路,记住方式二。
对于无限个、限制无效的,退化为完全背包,1个正序。
对于有限个、限制1个的,转化为多重背包,k个逆序。
/*
混合背包:每种物品个数可以是1个,也可以是K个,也可以是无限个(用0表示)
输入:
n 物品个数
m 背包容量
w[0] v[0] c[0] 重量,价值,数量
w[1] v[1] c[1] 重量,价值,数量
*
w[n-1] v[n-1] c[n-1] 重量,价值,数量
输出:
dp[m] 最大价值
*/
/*
输入1:
3
10
2 4 1
4 7 0
7 6 4
输出:
18
输入2:
3
10
2 7 3
4 5 0
7 6 1
输出:
26
输入3:
5
10
5 1 1
4 2 2
3 3 0
2 4 2
1 5 1
输出:
16
*/
#include
#include
using namespace std;
int main()
{
//输入
int n; //物品类数
int m; //背包容量
cin>>n>>m;
vector w(n); //重量
vector v(n); //价值
vector c(n); //数量
for(int i=0; i>w[i]>>v[i]>>c[i];
}
//dp
//* 方式一,N*K*M
//每件物品进行 K 次01背包
vector dp(m+1, 0); //一维数组,相当于第-1行(0个物品/没有物品)为全为0;0个物品,价值为0。
for(int i=0; i= m) //无限个,或者,限制无效,退化成完全背包
{
for(int j=w[i]; j<=m; j++) //完全背包,1次正序
{
dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
}
}
else //有限个,或者,1个,按多重背包
{
for(int k=1; k<=c[i] && k<=m/w[i]; k++) //多重背包,K次01物品,K次逆序
{
for(int j=m; j>=w[i]; j--)
{
dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
}
}
}
}
//*/
/* 方式二,N*log(K)*M
vector dp(m+1, 0);
for(int i=0; i= m)
{
for(int j=w[i]; j<=m; j++)
{
dp[j] = max(dp[j], dp[j-w[i]]+v[i]);
}
continue;
}
//否则,是多重背包
int number = c[i]; //物品数限制
for(int k=1; k<=number; k*=2) //放入 1,2,4,...,kk 个“本种物品”,且 sum = 1+2+4+...+kk <= number,然后判断剩下的 rest = number-sum 件物品
{
for(int j=m; j>=k*w[i]; j--)
{
dp[j] = max(dp[j], dp[j-k*w[i]] + k*v[i]);
}
number -= k; //剩余物品数,判断是否能继续取k件“本种物品”
}
for(int j=m; j>=number*w[i]; j--)
{
dp[j] = max(dp[j], dp[j-number*w[i]] + number*v[i]);
}
}
//*/
//输出
cout << dp[m] << endl;
return 0;
}
每种物品有两个费用。
/*
二维费用背包:每种物品有两个费用。
二维费用01背包:
二维费用完全背包:逆序 改为 正序
二维费用多重背包:加一个“物品数量层”,进行K次“二维费用01背包”。并且加入“退化为完全背包”的判断。
二维费用混合背包:
输入:
n 物品个数
m1 背包容量1
m2 背包容量2
w1[0] w2[0] v[0] 重量1,重量2,价值
w1[1] w2[1] v[1] 重量1,重量2,价值
*
w1[n-1] w2[n-1] v[n-1] 重量1,重量2,价值
输出:
dp[m1][m2] 最大价值
*/
/*
输入1:
3
10
10
2 3 4
4 2 7
7 4 6
输出:
11
*/
#include
#include
using namespace std;
//二维费用01背包
int main()
{
//输入
int n; //物品类数
int m1; //背包容量1
int m2; //背包容量2
cin>>n>>m1>>m2;
vector w1(n); //费用1
vector w2(n); //费用2
vector v(n); //价值
for(int i=0; i> w1[i] >> w2[i] >> v[i];
}
//dp
vector> dp(m1+1, vector(m2+1, 0)); //全部初始化为0
for(int i=0; i=w1[i]; j1--) //费用1,逆序
{
for(int j2=m2; j2>=w2[i]; j2--) //费用2,逆序
{
dp[j1][j2] = max(dp[j1][j2], dp[j1-w1[i]][j2-w2[i]] + v[i]);
}
}
}
//输出
cout << dp[m1][m2] << endl;
return 0;
}
//二维费用混合背包
int main_mixbag()
{
//输入
int n; //物品类数
int m1; //背包容量1
int m2; //背包容量2
cin>>n>>m1>>m2;
vector w1(n); //费用1
vector w2(n); //费用2
vector v(n); //价值
vector c(n); //数量(0代表无限)
for(int i=0; i> w1[i] >> w2[i] >> v[i] >> c[i];
}
//dp
vector> dp(m1+1, vector(m2+1, 0)); //全部初始化为0
for(int i=0; i= m1 && c[i]*w2[i] >= m2)) //无限个、限制无效,退化为完全背包,正序
{
for(int j1=w1[i]; j1<=m1; j1++)
{
for(int j2=w2[i]; j2<=m2; j2++)
{
dp[j1][j2] = max(dp[j1][j2], dp[j1-w1[i]][j2-w2[i]] + v[i]);
}
}
}
else //有限个,多重背包(加一个数量层),逆序
{
for(int k=1; k<=c[i] && k<=m1/w1[i] && k<=m2/w2[i]; k++)
{
for(int j1=m1; j1>=w1[i]; j1--) //费用1,逆序
{
for(int j2=m2; j2>=w2[i]; j2--) //费用2,逆序
{
dp[j1][j2] = max(dp[j1][j2], dp[j1-w1[i]][j2-w2[i]] + v[i]);
}
}
}
}
}
//输出
cout << dp[m1][m2] << endl;
return 0;
}
每组中只能选一个。
题解使用了方式一,“同种物品放几个“、”分组中放哪个“ 可以获得最大收益的思路,记住方式一。
/*
分组背包问题
* 每组中选一件
* 有 N 件物品和一个容量为 M 的背包。第i件物品的费用是w[i],价值是v[i]。
* 这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
dp[k][j] 表示 前k组物品 花费费用j 能取得的最大权值
dp[k][j] = max(dp[k−1][j], dp[k−1][j−w[i]] + v[i]); 物品i属于组k
dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
for(k=kmin; k<=kmax; k++) //分组
for(j=m; j>=0; j--) //容量
for(i) //分组的物品,i属于分组k
if(j>w[i])
dp[j] = max(dp[j], dp[j-w[i]] + v[i]);
输入:
n 物品个数
m 背包容量
w[0] v[0] k0 重量,价值,分组
w[1] v[1] k1 重量,价值,分组
*
w[n-1] v[n-1] k[n-1] 重量1,重量2,价值
输出:
dp[m] 最大价值
*/
/*
输入1:
6
10
2 1 1
3 2 1
4 2 2
5 3 2
2 4 3
1 3 3
输出:
9
*/
#include
#include
#include
所选则的物品需要刚好装满背包,求最大收益。
只需要改变初始化即可。
//非恰好背包,初始化
vector dp(m+1, 0); //0个物品,任何容量,收益为0
//恰好背包,初始化
vector dp(m+1, INT_MIN); //0个物品,不能装满其他位置,非法。
dp[0] = 0; //0个物品,恰好可以装满空背包,收益为0
计算之后,在 dp 数组结果中,INT_MIN 的为非法。
好链接
1、动态规划:完全背包、多重背包
https://blog.csdn.net/qq_38984851/article/details/81133840
2、背包九讲——全篇详细理解与代码实现
https://blog.csdn.net/yandaoqiusheng/article/details/84782655