背包九讲之——01背包

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背包的基本讲解了,若有不足还望海涵并指出

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值