背包问题
01 背包问题
求出限重在j情况下的 可以放入物品的最大价值V, 每个物品只有1个
初始化条件
vector weights = {1,3,5,4,6,4,2};
vector values = {2,4,6,3,1,5,5};
设定 d[i][j] 表示为 (前i个物品在限重j重量下所呈现的,前i个物品不清楚里面具体放了哪几个) 最大价值
设定 d[i - 1][j] 表示为 (前i个物品在限重j重量下所呈现的,前i -1 个物品不清楚里面具体放了哪几个) 最大价值
假如现在的状态是 d[i - 1][j], 已经明确了前i-1个物品所带来的最大价值,
那么第i个物品不放入背包的价值为
d[i][j] = d[i-1][j]
那么第i个物品选择放入背包的价值为
当前物品的价值 + 前i-1个物品在限重 [j - 当前物品重量下的限重] 的最大价值
d[i][j] = value[i]+ dp[i-1][j - wights[i]]
那么状态转移公式应该为 选择两者放入与不放入的中的最大值
d[i][j] = max ( d[i-1][j] , value[i]+ dp[i-1][j - wights[i]])
初始化dp[0][1,2,…j] = 0
我们手动推理写下这个过程
考虑前1个物体在限重0,1,2,3,…j 的 最大价值
d[1][0] = d[0][0] = 0
d[1][1] = max ( d[0][1], 2 + d[0][1 - 1]) = 2
d[1][2] = max ( d[0][2], 2 + d[0][2 - 1 ]) = 2
d[1][3] = max ( d[0][3], 2 + d[0][3 - 1]) = 2
d[1][4] = max ( d[0][4], 2 + d[0][4 - 1]) = 2
d[1][5] = max ( d[0][5], 2 + d[0][5 - 1]) = 2
d[1][6] = max ( d[0][6], 2 + d[0][6 - 1]) = 2
d[2][0] = d[1][0] = 0
d[2][1] = max ( d[1][1], 4 + d[1][1-3]) = 2
d[2][2] = max ( d[1][2], 4 + d[1][2-3]) = 2
d[2][3] = max ( d[1][3], 4 + d[1][0]) = 4
d[2][4] = max ( d[1][4], 4 + d[1][1]) = 6
d[2][5] = max ( d[1][5], 4 + d[1][2]) = 6
d[2][6] = max ( d[1][6], 4 + d[1][3]) = 6
d[3][0] = d[2][0] = 0
d[3][1] = max ( d[2][1] 2, 6 + d[2][1-5]) = 6
d[3][2] = max ( d[2][2] 2, 6 + d[2][2-5]) = 6
d[3][3] = max ( d[2][3] 2, 6 + d[2][3-5]) = 6
d[3][4] = max ( d[2][4] 6, 6 + d[2][4-5]) = 6
d[3][5] = max ( d[2][5] 6, 6 + d[2][0]) = 6
d[3][6] = max ( d[2][6] 6, 6 + d[2][1]) = 8
int package_01(int total_wieght)
{
// 01 背包问题 求限重j下装入物品的最大价值 每个物品只有一个, 不是无限个
vector<int> weights = {1,3,5,4,6,4,2};
vector<int> values = {2,4,6,3,1,5,5};
//这里开辟的空间是 NM 也就是 weights.size * total_wieght 的大小
vector<vector<int>> dp(weights.size() + 1, vector<int>(total_wieght + 1,0));
for(int i = 1; i < weights.size() + 1; i++)
{
for(int j = 1; j <= total_wieght; j++)
{
//由于从i 从 1 开始 所以这里的对应值应该减去1
int current_value = values[i -1];
int current_weight = weights[i-1];
if(current_weight <= j)
{
dp[i][j] = max(dp[i-1][j], current_value + dp[i-1][j-current_weight]);
}
else
{
dp[i][j] = dp[i-1][j];
}
printf("dp[%d][%d] = %d\n",i,j,dp[i][j]);
}
}
return dp[weights.size()][total_wieght];
}
继续观察这个代码 发现开辟的空间数组大小也可以优化 因为dp[i] 每次循环只依赖上次dp[i-1]的结果
只开辟dp[total_weight +1] 的空间就可以满足计算运行
更新公式如下
dp[j] = | max( d[j] , values[i] + dp[3-weights[i]] ) |
---|---|
假如这里代表dp[2][j] | 那么这里代表dp[1][j] 和 dp[1][j-weights[i]] |
大限重的value 计算 依赖了小限重的value
那么需要反过来从限重从大往小计算最大价值
j = J ,j-1,j-2,3,2,1
int package_01_small_space(int total_wieght)
{
// 01 背包问题 求限重j下装入物品的最大价值 每个物品只有一个, 不是无限个
vector<int> weights = {1,3,5,4,6,4,2};
vector<int> values = {2,4,6,3,1,5,5};
//这里开辟的空间是 NM 也就是 total_wieght + 1 的大小
int dp[total_wieght + 1] ={0};
for(int i = 1; i < weights.size() + 1; i++)
{
//倒序计算
for(int j = total_wieght; j >= 1; j--)
{
//由于从i 从 1 开始 所以这里的对应值应该减去1
int current_value = values[i -1];
int current_weight = weights[i-1];
if(current_weight <= j)
{
// not choose this object
dp[j] = max(dp[j], current_value + dp[j-current_weight]);
}
printf("dp[%d][%d] = %d\n",i,j,dp[j]);
}
}
return dp[total_wieght];
}
完全背包问题
01 完全背包问题 求限重j下装入物品的最大价值 每个物品有无限个 求 最大价值
dp[j] 限重j下的最大价值
dp[j-1] 限重j-1下的最大价值
状态转移方程为
for i in 1,2,3,4,N
dp[j] = max(value[i] + dp[j - weight[i]],)
int package_all(int total_wieght)
{
// 01 完全背包问题 求限重j下装入物品的最大价值 每个物品有无限个 求 最大价值
vector<int> weights = {1,3,5,4,6,4,2};
vector<int> values = {2,4,6,3,1,5,5};
// 设定 d[i][j] 表示为 前i个物品在j重量下所呈现的最大价值
/*
dp[j] 代表限重下的最大价值
dp[j] = max{value[i]+dp[j - weight[i]],.....}
*/
printf("\n\n");
int dp[total_wieght + 1] ={0};
for(int j = 1; j <= total_wieght; j++)
{
for(int i = 0; i < weights.size(); i++)
{
if(weights[i] <= j)
{
dp[j] = max(dp[j] ,values[i] + dp[j - weights[i]]);
}
}
printf("dp[%d] = %d\n",j,dp[j]);
}
return dp[total_wieght];
}
多重背包问题
多重背包问题
本质分解为
前i个物品在限重下的最大价值
和本次一次性装入k个物品的最大价值 比较
int package_repeated(int total_wieght)
{
// 01 多重背包问题 求限重j下装入物品的最大价值 每个物品有N个 求 最大价值
vector<int> weights = {1,3,5,4,6,4,2};
vector<int> values = {2,4,6,3,1,5,5};
vector<int> counts = {2,1,2,3,1,2,3};
// 设定 d[i][j] 表示为 前i个物品在j重量下所呈现的最大价值
/*
k 表示装入 k个 i物品
总数量 可以装入的最大数量 两者取最小值
k < min ( counts[i], j / weights[i] )
dp[i][j]
转移公式
装入k个物品 不装入k个物品, 没有第几个
dp[i][j]
= max(dp[i-1][j], value[i] * k + dp[i-1][j - k * weight[i]])
*/
int dp[total_wieght + 1] ={0};
for(int j = 1; j <= total_wieght; j++)
{
for(int i = 0; i < weights.size(); i++)
{
for(int k = 0 ; k <= counts[i]; k ++)
{
if( k * weights[i] <= j)
{
dp[j] = max(dp[j] ,k * values[i] + dp[j - k * weights[i]]);
}
}
}
printf("dp[%d] = %d\n",j,dp[j]);
}
return dp[total_wieght];
}
恰好装满问题
恰好装满背包
与01 背包区别是初始化过程中 dp[0] = 0 dp[others] = -inf
int package_case(int total_wieght)
{
// 01 恰好装满背包
vector<int> weights = {3,5,4,6};
vector<int> values = {4,6,6,1};
vector<int> counts = {1,2,3,1,2,3};
// 设定 d[i][j] 表示为 前i个物品在j重量下所呈现的最大价值
/*
dp[0] = [0]
dp[1] = -inf
dp[2] = 5
//完全背包
dp[3] = max(4, 5 + dp[1]) = max(4,5+0) = 5
//恰好背包
dp[3] = max(4, 5 + dp[1]) = max(4,5+ -inf) = 4
*/
vector<int> dp(total_wieght+1,std::numeric_limits<int>::min());
// 所以对int数组进行非0赋值或初始化时不能用memset()函数,只能循环处理/定义时初始化
dp[0] = 0;
for(int j = 1; j <= total_wieght; j++)
{
for(int i = 0; i < weights.size(); i++)
{
if(weights[i] <= j)
{
dp[j] = max(dp[j] ,values[i] + dp[j - weights[i]]);
}
}
printf("dp[%d] = %d\n",j,dp[j]);
}
return dp[total_wieght];
}
求方案总数
// 方案总数 求所有方案 台阶问题
https://blog.csdn.net/zhuanzhe117/article/details/72846939
int package_sum(int total_wieght)
{
// 01 上台阶 上100个台阶的总方案数目
vector<int> weights = {3,5,4,6,2};
vector<int> dp(total_wieght+1,0);
dp[0] = 1;
/*
https://blog.csdn.net/zhuanzhe117/article/details/72846939
dp[j] = sum(dp[j-3],dp[j-5],dp[j-4],dp[j-6],dp[j-2])
*/
// 所以对int数组进行非0赋值或初始化时不能用memset()函数,只能循环处理/定义时初始化
for(int j = 1; j <= total_wieght; j++)
{
for(int i = 0; i < weights.size(); i++)
{
if(weights[i] <= j)
{
dp[j] = dp[j] + dp[j - weights[i]];
}
}
printf("dp[%d] = %d\n",j,dp[j]);
}
return dp[total_wieght];
}
#include <iostream>
#include <string>
#include <vector>
#include <limits>
using namespace std;
int main(int argc, char *argv[])
{
//solutions(10);
// std::vector<int> coins = {1,3,5};
// std::cout << Solution::change(4,coins);
printf("argv %s\n",argv[1]);
int a = atoi(argv[1]);
int res = package_01_small_space(a);
printf("\npackage_01_small_space %d\n",res);
res = package_01(a);
printf("\npackage_01 %d\n",res);
}
参考链接
https://zhuanlan.zhihu.com/p/93857890
https://blog.csdn.net/zhuanzhe117/article/details/72846939