背包问题
. 0/1 背包问题
题目
01背包问题描述:有编号分别为a,b,c,d,e的五件物品,
它们的重量分别是2,2,6,5,4,
它们的价值分别是6,3,5,4,6,
每件物品数量只有一个,
现在给你个承重为c=10的背包,如何让背包里装入的物品具有最大的价值总和?
思路
代码
int knapsack_01(vector<int>&W, vector<int>&V, int C){
if (W.size() != V.size()) return -1;
if (W.size() < 1 || V.size() < 1 || C < 0) return -1;
//开辟动态规划的空间,dp[i][cc] 表示使用前i个硬币,放入容量为c的背包的最大价值
int n = W.size(); //商品种类
vector<vector<int>> dp(n, vector<int>(C+1 , 0)); //【n,c+1]大小的矩阵
//初始化第一行,第一列
//第一行表示,只使用商品1,放入容量为1,2,....c的包中的最大价值
for (int cc = 0;cc <= C; cc++){
if (W[0] <=cc)
dp[0][cc] = V[0];
}
第一列表示,使用商品1,2,...,商品放入容量为0的包最大价值,都为0,已经初始化为0了,不用重复操作
//for (int i = 0; i < n; i++){
// dp[i][0] = 0;
//}
//递推
/*
选择第i个商品, 则s1 = dp[i-1][cc-wi]+vi
不选择第i个商品,则s2 = dp[i-1][cc]
dp[i][cc] =max(s1,s2); //选择最优
*/
for (int i = 1; i < n; i++){
for (int cc = 1; cc <= C; cc++){
//因为内层遍历的包的大小从1到C连续编号,对于某个商品wi来说,可能包的容量cc比较小的时候,是没法放入容量为cc的包的,只能不选择第i个商品,则s = dp[i-1][cc]
if (W[i] >cc){
dp[i][cc] = dp[i - 1][cc]; //放不小,则不放入
}
else{
dp[i][cc] = max(dp[i - 1][cc],
dp[i - 1][cc - W[i]] + V[i]); //能放入,则决策是否要真的要放入
}
}
}
//打印观察
for (int i = 0; i < dp.size(); i++){
printf("使用前%d个商品:",i);
for (int j = 0; j < dp[0].size(); j++){
printf("%4d ", dp[i][j]);
}
printf("\n");
}
//返回最后结果
return dp[n - 1][C];
}
.完全背包问题
题目
完全背包问题:
完全背包问题描述:有编号分别为a,b,c,d的四件物品,
它们的重量分别是2,3,4,7,
它们的价值分别是1,3,5,9,
每件物品数量无限个,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?
思路
代码
int knapsack_complete(vector<int>&W, vector<int>&V, int C){
if (W.size() != V.size()) return -1;
if (W.size() < 1 || V.size() < 1 || C < 0) return -1;
//开辟动态规划的空间,dp[i][cc] 表示使用前i个硬币,放入容量为c的背包的最大价值
int n = W.size(); //商品种类
vector<vector<int>> dp(n, vector<int>(C + 1, 0)); //【n,c+1]大小的矩阵
//初始化第一行,第一列
//第一行表示,只使用商品1,放入容量为1,2,....c的包中的最大价值
for (int cc = 0; cc <= C; cc++){
dp[0][cc] = (cc / W[0])*V[0]; //商品1可以重复使用,重复个数为cc/W[0]
}
第一列表示,使用商品1,2,...,商品放入容量为0的包最大价值,都为0,已经初始化为0了,不用重复操作
//for (int i = 0; i < n; i++){
// dp[i][0] = 0;
//}
//递推
/*
选择第i个商品, 则s1 = dp[i][cc-wi]+vi //注意dp[i]和完全背包中的dp[i-1]不同,使用dp[i]表示即使当前行中已经使用的背包i,在该行中仍然可以继续使用
不选择第i个商品,则s2 = dp[i-1][cc]
dp[i][cc] =max(s1,s2); //选择最优
*/
for (int i = 1; i < n; i++){
for (int cc = 1; cc <= C; cc++){
//因为内层遍历的包的大小从1到C连续编号,对于某个商品wi来说,可能包的容量cc比较小的时候,是没法放入容量为cc的包的,只能不选择第i个商品,则s = dp[i-1][cc]
if (W[i] >cc){
dp[i][cc] = dp[i - 1][cc]; //放不小,则不放入
}
else{
dp[i][cc] = max(dp[i - 1][cc],
dp[i ][cc - W[i]] + V[i]); //能放入,则决策是否要真的要放入,当前行中比较,表示可以重复使用
}
}
}
//打印观察
for (int i = 0; i < dp.size(); i++){
printf("使用前%d个商品:", i);
for (int j = 0; j < dp[0].size(); j++){
printf("%4d ", dp[i][j]);
}
printf("\n");
}
//返回最后结果
return dp[n - 1][C];
}
.多重背包问题
题目
多重背包问题:
多重背包问题描述:
有编号分别为a,b,c的三件物品,
它们的重量分别是1,2,2,
它们的价值分别是6,10,20,
他们的数目分别是10,5,2,现在给你个承重为 8 的背包,如何让背包里装入的物品具有最大的价值总和?
思路
多重背包和01背包、完全背包的区别:多重背包中每个物品的个数是有限的。
初始化时,
只考虑一件物品a时,
count = min{num[1], cc/weight[1]}。
f[1][cc] = count* v[1];
计算考虑i件物品承重限制为y时最大价值f[i][cc]时,递推公式考虑两种情况,
要么第 i 件物品一件也不放,就是f[i-1][cc],
要么第 i 件物品放 k 件,其中 1 <= k <= count, 其中count=min{num[i], cc/weight[i]}
考虑这一共 k+1 种情况取其中的最大价值即为f[i][y]的值,
即f[i][cc] = max{f[i-1][cc],
f[i-1][cc-kweight[i]]+kvalue[i]}。
代码
int knapsack_multi(vector<int>&W, vector<int>&V, vector<int>&N, int C){
if (W.size() != V.size()) return -1;
if (W.size() < 1 || V.size() < 1 || C < 0) return -1;
//开辟动态规划的空间,dp[i][cc] 表示使用前i个硬币,放入容量为c的背包的最大价值
int n = W.size(); //商品种类
vector<vector<int>> dp(n, vector<int>(C + 1, 0)); //【n,c+1]大小的矩阵
//初始化第一行,第一列
//第一行表示,只使用商品1,放入容量为1,2,....c的包中的最大价值
for (int cc = 0; cc <= C; cc++){
int count = min(cc / W[0], N[0]); //商品1可以重复使用,重复个数在cc/W[0]和总个数N[0]中取最小值
dp[0][cc] = (count)*V[0];
}
第一列表示,使用商品1,2,...,商品放入容量为0的包最大价值,都为0,已经初始化为0了,不用重复操作
//for (int i = 0; i < n; i++){
// dp[i][0] = 0;
//}
//递推
/*
要么第 i 件物品一件也不放,就是f[i-1][cc],
要么第 i 件物品放 k 件,其中 1 <= k <= count, 其中count=min{num[i], cc/weight[i]}
考虑这一共 k+1 种情况取其中的最大价值即为f[i][y]的值,
即f[i][cc] = max{f[i-1][cc],
f[i-1][cc-k*weight[i]]+k*value[i]}。
*/
for (int i = 1; i < n; i++){
for (int cc = 1; cc <= C; cc++){
if (W[i]>cc){ //一个都放不下的时候
dp[i][cc] = dp[i - 1][cc];
}
else //能放下多个的时候
{
int count = min(cc / W[i], N[i]); //商品i可以重复使用,重复个数在cc/W[i]和总个数N[i]中取最小值
dp[i][cc] = dp[i - 1][cc]; //不使用第i个商品时
for (int k = 1; k <= count; k++){ //第i个商品使用多次,在这多次中挑一个使得总价值最大
int temp = dp[i - 1][cc - k*W[i]] + k*V[i]; //放入k个商品i时的价值
if (temp > dp[i][cc]) //找最大价值
dp[i][cc] = temp;
}
}
}
}
//打印观察
for (int i = 0; i < dp.size(); i++){
printf("使用前%d个商品:", i);
for (int j = 0; j < dp[0].size(); j++){
printf("%4d ", dp[i][j]);
}
printf("\n");
}
//返回最后结果
return dp[n - 1][C];
}