一,问题
【问题】
一个容量为m公斤的背包。现有n种物品,每种物品只有一件,它们的重量分别为w[ i ](1<=i<=n),它们的价值分别为c[ i ](1<=i<=n)。求能放入背包的最大价值。
【输入】
第一行:两个整数,n(物品数量,n<51)和m(背包容量,m<201)。
第2,3,,,n+1行:每行两个整数w[i],c[i],表示每个物品的重量和价值。
【输出】
一个数,表示最大价值。
【样例输入】
3 6
3 5
2 3
4 6
【样例输出】
9
PS:
01背包:第 i 件物品可以放入0个或1个;
完全背包:第 i 件物品可以放入0个,1个,2个.........
二,解答思路
1,确定状态变量(函数);
2,确定状态转移方程(递推关系);
3,确定边界条件。
最大价值是物品数量 i 和背包容量 j 的函数。
设函数 f[ i ][ j ] 表示前 i 件物品放入容量为 j 的背包的最大价值。
最终的最大价值就是物品数量 i 从0增长到n,背包容量 j 从0增长到m时的 f[ n ][ m ] 的值。
若当前背包容量为 j ,我们就要考虑第 i 件物品能否放入?是否放入?
1,当前背包容量 j<W[i], 不能放入,则 f[ i [ j ]=f[ i-1 ][ j ];
2,当前背包容量 j>=W[i],能放入,但要比较价值:
(1)如果第 i 件物品不放入背包,则 f[ i ][ j ]=f[i-1][ j ];
(2)如果第 i 件物品放入背包,则f[ i ][ j ]=f[ i -1 ][ j-w[ i ] ]+c[ i ]
如果第 i 件物品放入背包,则背包容量还剩 j-w[ i ],所以要取前 i-1 件物品放入背包,剩余容量 j-w[ i ]所获得的最大价值为 f[ i-1 ][ j-w[i] ]。
举个例子:
状态转移方程:
f[ i ][ j ]=f[ i-1 ][ j ],(j<w[ i ])
f[ i ][ j ]=max(f[ i-1 ][ j ],f[ i-1 ][ j-w[i] ]+c[ i ],(j>=w[ i ])
边界条件:f[ i ][ j ]=0
三,代码
for(i=1;i<=n;i++)//物品 i
{
for(j=1;j<=m;j++)//容量 j
{
if(j<w[i])
f[i][j]=f[i-1][j];
else
f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+c[i]);
}
}
printf("%d",f[n][m]);
作进一步的优化:
用一维数组 f[ j ] 只记录一行数据。让 j 值顺序循环,顺序更新 f[ j ] 的值。
最终简化为:
for(i=1;i<=n;i++)//物品 i
{
for(j=w[i];j<=m;j++)//容量 j
{
f[j]=max(f[j],f[j-w[i]]+c[i]);
}
}
printf("%d",f[m]);
因为 j 是逆序循环,f[ j ]会先于 f[ j-w[i] ]更新,也就是说,用旧值 f[ j-w[i] ]去更新 f[ j ],相当于用上一行的 f[ j-w[i] ]去更新 f[ j ]。