关于背包问题,其实可以分为两种类型:0-1背包问题(动态规划) 和 部分背包问题(贪心算法)。在这里仅介绍01背包。
- 0-1背包问题:每件物品或被带走,或被留下,(需要做出0-1选择)。小偷不能只带走某个物品的一部分或带走两次以上同一个物品。
在选择是否要把一个物品加到背包中,必须把该物品加进去的子问题的解与不取该物品的子问题的解进行比较。这种方式形成的问题导致了许多重叠子问题,满足动态规划的特征。
- 部分背包问题:小偷可以只带走某个物品的一部分,不必做出0-1选择。
总是选择每一磅价值 (Vi / Wi) 最大的物品添加进背包中。那么其解决过程是:对每磅价值进行排序,依次从大到小选择添加进背包中
0-1背包
题目:有一个容量为 V 的背包,和一些物品。这些物品分别有两个属性,体积 w 和价值 v,每种物品只有一个。要求用这个背包装下价值尽可能多的物品,求该最大价值,背包可以不被装满。
0-1背包问题:在最优解中,每个物品只有两种可能的情况,即在背包中或者不在背包中(背包中的该物品数为0或1),因此称为0-1背包问题。
步骤1-找子问题:子问题必然是和物品有关的,对于每一个物品,有两种结果:能装下或者不能装下。第一,包的容量比物品体积小,装不下,这时的最大价值和前i-1个物品的最大价值是一样的。第二,还有足够的容量装下该物品,但是装了不一定大于当前相同体积的最优价值,所以要进行比较。由上述分析,子问题中物品数和背包容量都应当作为变量。因此子问题确定为背包容量为j时,求前i个物品所能达到最大价值。
步骤2-确定状态:由上述分析,“状态”对应的“值”即为背包容量为j时,求前i个物品所能达到最大价值,设为dp[i][j]。初始时,dp[0][j](0<=j<=V)为0,没有物品也就没有价值。
步骤3-确定状态转移方程:由上述分析,第i个物品的体积为w,价值为v,则状态转移方程为
j<w,dp[i][j] = dp[i-1][j] //背包装不下该物品,最大价值不变
j>=w, dp[i][j] = max{ dp[i-1][j-list[i].w] + v, dp[i-1][j] } //和不放入该物品时同样达到该体积的最大价值比较
#include<cstdio>
int max(int a, int b)//取最大值函数
{
return a > b ? a : b;
}
struct Thing
{
int w;
int v;
}list[101];
int dp[101][1001];
int main()
{
int s, n;//背包容量和物品总数
while (scanf("%d%d", &s, &n) != EOF)
{
for (int i = 1; i <= n; i++)
{
scanf("%d%d", &list[i].w, &list[i].v);//读入每个物品的体积和价值
}
for (int i = 0; i <= s; i++) dp[0][i] = 0;//初始化二维数组
for (int i = 1; i <= n; i++)//循环每个物品,执行状态转移方程
{
for (int j = s; j >= list[i].w; j--)
{
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - list[i].w] + list[i].v);
}
for (int j = list[i].w - 1; j >= 0; j --)
{
dp[i][j] = dp[i - 1][j];
}
}
printf("%d\n", dp[n][s]);
}
return 0;
}