01背包问题c++动态规划 代码及讲解

这篇博客是我为了巩固自己的理解写出来的,所以如果我写的有错误,有含糊的地方,也欢迎各位大佬前来指正,我将感激不尽!


前言

动态规划(Dynamic Programming 简称:DP)是一种通过将复杂的问题分解成已解决的子问题,来解决原问题的方法,它的好处是可以 避免重复计算子问题,因为动态规划会将已经解决的子问题的答案存储下来,等下次需要用到的时候直接读表就可以了


一、什么是01背包?

01背包是一个典型的动态规划问题,问题的目的是在有限的背包容量下,找到能装的最大价值,问题的特点有两个:1、求最大价值,2、每种物品只能选择一次

二、分析问题

题目

小明去超市购物,超市里搞"零元购"活动,能拿多少拿多少,但是每件只能拿一次,给定n种商品,小明的购物袋只能放m千克的的物品,n件物品的重量分别是w1,w2,w3…wn,价值分别是v1,v2,v3…vn,求小明所能获得的物品的最大价值
数据范围:
n<=3500
m<=12800
样例输入:
4 10
2 1
3 3
4 5
7 9
样例输出:
12

表示状态

现在建立一个数组 dp[n][m] 用来存子问题的解,那么 dp[i][j] 就表示 将前 i 件物品放入容量为 j 的背包里的最大价值,这句话是重点哦同志们,理解了这句话你的01背包问题就已经解决了一半了
再建立两个数组 w[n]v[n],分别用来存储 物品的重量物品的价值

状态转移方程

01背包问题的一大特性在于:每件商品只能选择一次,根据这个特性我们可以得出每一件商品在选择时 只有选与不选这两种可能

首先来看不选
什么情况下会不选呢?只有在放不下的时候会不选(当前物品重量大于当前背包容量),也只能延续上一件物品的选法

dp[i][j]=dp[i-1][j];//dp[i-1][j]表示的是将前i-1项物品放入容量为j的背包里的最大价值

然后再来看选
dp[i][j] 里放的要是最大价值,那么放了之后的价值要是比放之前还小应该怎么办呢?
所以我们需要比较大小,光说可能很难解释的清,所以直接上代码

dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);//max的前一项是放之前,后一项是防了之后

相信你看到这里一定是一脸懵逼,你肯定在想:这个 dp[ i-1 ][ j - w[i] ]+v[i] 是个什么鬼?
我们把它拆解着来看,先看后面的 +v[i],v[i]表示的是 第i个物品的价值+v[i] 也就是加上当前物品的价值

可是我们要算的是 将前 i 件物品放入容量为 j 的背包里的最大价值,要算 最大价值 就当然不只当前物品的价值,因为你的当前的背包放了第 i 件物品后,可能还会有 剩余的空间,这个剩余的空间就是 j - w[i] ,所以 dp[ i - 1 ][ j - w[i] ] 就表示 前 i-1 项物品里,加上了当前物品后,的剩余空间,的最大价值,这样说可能会有点绕,我来总结一下:

不选:延续上一次的最大价值
选:这一次的物品价值+剩余容量的最大价值

这样就清晰明了多了吧,那废话不多说,我们直接上代码:

三、解决问题(代码—未优化版)

#include<bits/stdc++.h>
using namespace std;

int n,m;
int w[3501],v[3501],dp[3501][12881];
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>w[i]>>v[i];
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(w[i]>j)	//不选
			{
				dp[i][j]=dp[i-1][j];
			}
			else	//选
			{
				dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
			}
		}
	}
	cout<<dp[n][m];
}

但是这个代码未免占用的空间太多了,我们来把他压缩一下
因为我们 dp[i][j] 的行只用了 i ~ i -1的范围,所以我们可以把它压缩成一个一维数组

最终版(代码)

#include<bits/stdc++.h>
using namespace std;

int n,m;
int w[3501],v[3501],dp[12881];
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>w[i]>>v[i];
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=m;j>=w[i];j--)
		{
			dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
		}
	}
	cout<<dp[m];
}

j 循环需要倒叙遍历,不然的话 j-w[i] 的值会被先刷新,这样会导致答案错误
由于是倒叙,我们选择直接从 m~ w[i],这样的背包容量就一定可以放下物品,就一定可以选,所以就把 if 判断去掉了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值