01背包
01背包是最基础的背包问题,其中01代表的就是第i个物品的选或不选,在此先设v[i]为体积,w[i]为价值。
很显然,我们可以使用二位数组dp[i][j]来表示前i个物品在背包容量为j的时候可存放的最大价值。首先dp[0][0]=0是很显然的。而计算dp[i][j]时,存在01两种情况:选或不选第i件物品。
1.不选第i件物品:dp[i][j]=dp[i-1][j];那么此时的价值就是前i件物品容量为j的时候价值,不难理解。
2.选第i件物品:dp[i][j]=dp[i-1][j-v[i]]+w[i];此时的价值为前i件物品容量为j-v[i]的容量。
由此得出状态转移方程:dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);遍历计算出dp[n][m]即可。
dp[0][0]=0;
for(int i=1;i<=n;i++)//n为物品数
{
for(int j=0;j<=m;j++)//m为背包的最大容量
{
dp[i][j]=dp[i-1][j];//先假设不选
if(j>=v[i])//如果j>=v[i],判断是否需要选
dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);
}
}
cout<<dp[n][m];//dp[n][m]显然为结果
由上可以看出为O(n*m)的复杂度,可以优化,那么二位的dp优化的方向便是向一维优化。由dp[i][j]->dp[j],表示容量为j的背包所装物品的最大价值,从上述基础代码做优化。
dp[0]=0;
for(int i=1;i<=n;i++)//n为物品数
{
for(int j=m;j>=v[i];j--)
{
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
/*考虑为什么内层循环变成相反方向循环?
因为若是从小往大循环,那么dp[j]=max(dp[j],dp[j-v[i]]+w[i]);中的dp[j-v[i]],j-v[i]是小于j的,
因此dp[j-v[i]]在本次i的循环中已经先于j被计算过,
那就是说dp[j-v[i]]可能其中已经包含了第i件物品,
那么再次计算dp[j]时可能会重复加上第i件物品;
换句话说,此时的dp[j-v[i]]是dp[i][j-v[i]]
而非dp[i-1][j-v[i]]。当反向从大到小遍历时
则会避免这个问题。*/
}
cout<<dp[m];
以上就是01背包的基本讲解了,若有不足还望海涵并指出