关于01背包问题的三种DP模式
题目模型:
有n个重量和价值分别为wi,vi的物品。从这些物品中挑选出总重量不超过W的物品,求所有挑选方案中价值总和的最大值。
限制条件:
n∈[1,100],
wi,vi∈[1,100],
W∈[1,10000].
输入
n = 4
(wi,vi) = {(2,3), (1,2), (3,4), (2,2)}
W = 5
输出
7(选择第0、1、3号物品)
本题我们不写递归函数,直接利用递推式将各项的值算出来,二重循环解决。
通过使用DP数组,我们可以完成所有结果的存取。
DP方法一:
我们记dp[i][j]为 从第i个物品开始挑选总重小于j时,总价值最大值。实现dp的代码如下:
void solve1()
{//从第i个物品开始挑选总重小于j时,总价值最大值为dp[i][j]
for (int i = n - 1; i >= 0; i--)
{//i从最后一项开始计算
for (int j = 0; j <= W; j++)
{
if (j < w[i])
{//第i个物品重量超重,跳过i,从i+1开始递归
dp[i][j] = dp[i + 1][j];
}
else
{//第i个物品不超重,挑选或不挑选,比较两者最大值存入dp
dp[i][j] = max(dp[i + 1][j], dp[i + 1][j - w[i]] + v[i]);
}
}
}
cout << dp[0][W];//输出结果
}
DP方法二:
我们记dp[i+1][j]为 从0~i这i+1个物品中选出总重量不超过j的物品时总价值的最大值。实现dp的代码如下:
void solve2()
{//从0~i这i+1个物品中选出总重量不超过j的物品时总价值最大值为dp[i+1][j]
for (int i = 0; i < n; i++)
{
for (int j = 0; j <= W; j++)
{
if (j < w[i])
{//超重说明当前第i个物品选不了
//状态转移为:从0~i-1这i个物品中选出总重量不超过j的物品时
//总价值最大值 == 从0~i这i+1个物品中选出总重量不超过j的
//物品时总价值最大值
dp[i + 1][j] = dp[i][j];
}
else
{//选i不选i比较一下
dp[i + 1][j] = max(dp[i][j], dp[i][j - w[i]] + v[i]);
}
}
}
cout << dp[n][W];//输出结果
}
DP方法三:
我们记dp[i][j]为 从前i个物品中选取总重不超过j的物品时总价值的最大值。实现的代码如下:
void solve3()
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j <= W; j++)
{
dp[i + 1][j] = max(dp[i + 1][j], dp[i][j]);
//先在前i个物品和前i+1个物品前考虑一下最大值
if (j + w[i] <= W)
{
dp[i + 1][j + w[i]] = max(dp[i + 1][j + w[i]], dp[i][j] + v[i]);
//前i+1个(不包括i+1)物品中选取总重不超过j+w[i]时,所对应最大值
//应当为:选取了第i个物品 或 不选取第i个物品
//两种情况下的最大值
}
}
}
cout << dp[n][W];//输出结果
}
第三种DP的状态转移为:从前i个物品中选取总重不超过j时的状态
转移至 前i+1个物品中选取总重不超过j 或前i+1个物品中选取总重不超过j+w[i]。