用XLSX工作表解释01背包问题的原理

很经典的问题啦,来讲讲我自己的思路

题目如下:

(别一看到旅行者就原神启动......玩原神玩的)

咳咳,步入正题,这题要用到动态规划,后面遇到的题如果有“最大最下”可能就是DP

先定义一个结构体,还有个结构体数组,数组下标代表第几种物品

struct goods{
	int W;
	int C;
}good[35];//物品的信息:重量和价值 

还有必要的数据(输入)

int M,N;//空间大小为M的背包和件数为N的物品 

那么接下来先输入

for(int i=1;i<=N;i++){
	cin>>good[i].W>>good[i].C; 
}//1~N的物品数据 

然后定义一个val_max,来存储最大的价值

int val_max=0;

接下来定义一个二维数组dp,参数如下:

int dp[N+5][M+5];//表示在[物品总种类为i][物品总空间为j]时价值的最大值 

一维的数字表示目前物品总种类(或许是数量?),二维数字则是表示目前分配给这些(个)物品的空间,这个数组对应的每个值意义为当目前物品总种类为i,目前分配给这些(个)物品的空间为j时,总价值的最大值

那么就开始循环了(原理往后看)

for(int i=0;i<=N;i++){//枚举种类  //注意此处枚举从0开始而不是从1 
		for(int j=0;j<=M;j++){//枚举空间 
			if(i==0){
				dp[i][j]=0;
			}//这里原先没写出问题了  //第0个物品不存在捏 
			else{
				if(j>=good[i].W){//dp[i-1][j]物品种类i-1,总共装了j的空间
				//dp[i-1][j-good[i].W]物品种类i-1装了j-good[i].W的空间,加上good[i].W空间的价值good[i].C的第i种物品 
					dp[i][j]=max(dp[i-1][j],dp[i-1][j-good[i].W]+good[i].C);
				}//物品种类为i时,max(这一次不拿的价值,这一次拿的价值) 
				else if(j<good[i].W){
				dp[i][j]=dp[i-1][j];//可以先后面的元素,推前面的元素 
				}
			}
		}
	}

第一个for就是在枚举各个物品,第二个for就是在枚举空间

然后下面这里if(i==0),因为我们上面输入的时候物品是从第一种开始而不是从第0种,需要注意

接着的else就是从第一个物品开始到第N个物品了

里面的if表示判断此时枚举出的空间够不够装

然后下面的这个式子,就是到了目前这一种物品,价值最大值是拿这种物品还是不拿这种物品

if后面这个else if其实就是逻辑上好看出来那么一小点......罢了

就是说此时枚举的空间不能装下第i个物品,所以此时最大值与装第i-1个物品时相同

经过循环能计算出所有的最大值,然后再用循环去记录最大值,或者是前面循环里面顺便记下来也行

for(int i=1;i<=N;i++){
	for(int j=1;j<=M;j++){
		val_max=max(val_max,dp[i][j]);
	}
}

大抵如此了,然后cout,最后return 0。

原理:

以样例为例,不对,样例不就是例子吗?

反正这里的表是样例

那么这里,背包大小为10(别强迫症要去问单位是什么,我不到啊),但是图画的是背包为四(别问为什么问就是画错了)。四种物品,重量和价值都有

那么写个表,这里用的是WPS表格:

这个表的意思就是:横表示分配给当前种类物品的空间,竖表示...emmm,我好像不是很能解释清楚,举个例子就知道了

对了,填入的数字是总的最大价值

我们可以看到,样例输入中,第1个物品的重量W为2,价值C为1,那么我们来给这个物品分配空间:

首先第零个物品和分配空间为零时,总价值都是0,所以上图填充0

接下来第2行,我们有1个第1种物品,那我们不给他空间就什么都装不下去,就是零

那么我们给第1个物品分配一单位的空间,但是第1个物品的重量是2单位,放不进去,所以此时价值为零

就是这样的

接着就分配给这个物品2个单位的空间,哎嘿,刚刚好放得下,那么此时背包里面有1个重量为2单位的物品,价值为1,填入1。

以此类推,后面分配3单位和4单位的空间,但是要注意这种物品只有1个哦,所以4空间里面虽然可以放两个这样的物品但是价值依旧是1而不是2,因为虽然空间够了但是这种物品只有1个

什么,如果有数据相等的?那就当作另一种物品,结果一样的

表填到这里了

那么接着下一行,这里的意思是有第1种和第2种物品的时候,分配给这两种物品的总共空间

由样例得,第2种物品的重量M为3,价值C为3,前面第1种物品的重量M为2,价值C为1

那么先分配给一个空间:

第1个物品放不下,第2个也放不下,所有总价值为0。

那么给两空间呢?放不下第2个但是放得下第1个,所以价值为1

那么不一样的来了:如果空间为3,那么我们选哪个呢?刚刚好可以放进去一个第2种物品或者是第1种物品,价值分别为3和1

显然的,3更大,那么填入3

前面都说啦是这个时候价值的最大值啦~

那么4空间呢?是什么呢?如下图

这又是什么意思?你看:4空间,前2种物品总的重量总共是不是5?那,只能把第2种物品放进去背包里面,总价值就是3。

不妨想想看,要是分配的空间为5呢?

那两种物品刚刚好放进去,总价值九位1+3=4,表中对应的空就是4而不是3。

以此类推,表格如下:

对了,这里背包变成10试试找找规律(其实写到这里才发现前面写的背包空间是4(捂脸))

样例就是上面的样例,懒得划上去找我这里告诉你一下

共四种物品,第一种 W=2 C=1

                     第二种 W=3 C=3

                     第三种 W=4 C=5

                     第四种 W=7 C=9

试试看?试试看嘛,请不要露出这样的表情

那么转化成代码咋样呢?回到这个图

还记得前面代码的dp吗?

int dp[N+5][M+5];//表示在[物品总种类为i][物品总空间为j]时价值的最大值 

还有这个循环

for(int i=0;i<=N;i++){//枚举种类  //注意此处枚举从0开始而不是从1 
		for(int j=0;j<=M;j++){//枚举空间 
			if(i==0){
				dp[i][j]=0;
			}//这里原先没写出问题了  //第0个物品不存在捏 
			else{
				if(j>=good[i].W){//dp[i-1][j]物品种类i-1,总共装了j的空间
				//dp[i-1][j-good[i].W]物品种类i-1装了j-good[i].W的空间,加上good[i].W空间的价值good[i].C的第i种物品 
					dp[i][j]=max(dp[i-1][j],dp[i-1][j-good[i].W]+good[i].C);
				}//物品种类为i时,max(这一次不拿的价值,这一次拿的价值) 
				else if(j<good[i].W){
				dp[i][j]=dp[i-1][j];//可以先后面的元素,推前面的元素 
				}
			}
		}
	}

其实细细看,上面我们写的表格,长11,宽5;这里的数组一维为4+5=9,二维为10+5=15,那么就有对应关系了:dp数组其实就是上面这个表格,相应位置的数字都一样呢!

那么把表格和dp一一对应,这里dp实际上表示的是比11*5要大的,但是数组开大一点也是为了避免越界(虽然+1就可以了这个看看自己,但是一定要开够

下面是表格和dp的对应

原来的表

和dp对应的表

不好和代码对应的话再把代码搬过来

for(int i=0;i<=N;i++){//枚举种类  //注意此处枚举从0开始而不是从1 
		for(int j=0;j<=M;j++){//枚举空间 
			if(i==0){
				dp[i][j]=0;
			}//这里原先没写出问题了  //第0个物品不存在捏 
			else{
				if(j>=good[i].W){//dp[i-1][j]物品种类i-1,总共装了j的空间
				//dp[i-1][j-good[i].W]物品种类i-1装了j-good[i].W的空间,加上good[i].W空间的价值good[i].C的第i种物品 
					dp[i][j]=max(dp[i-1][j],dp[i-1][j-good[i].W]+good[i].C);
				}//物品种类为i时,max(这一次不拿的价值,这一次拿的价值) 
				else if(j<good[i].W){
				dp[i][j]=dp[i-1][j];//可以先后面的元素,推前面的元素 
				}
			}
		}
	}

别忘了good[i]是第i个物品的数据,是个结构体,W是重量,C是价值哦

看一看,想一想,让智慧之神给予你力量吧

等下,我先说好,我不玩原神

。。。

。。。

。。。

咳咳,那么就到这里了,最主要的部分就是这里的表格了,对了,你看看前面的那个表格,最大值是不是12?样例输出是不是12捏?

针对这个题,DP的思想也体现出来了

(下面的东西不一定对)

和贪心不同,如果一直求子问题最优解,比如这里:

如果按照贪心的思想,这里下面的图填入的值之前的问题,最优解是1,那么按照前面的最优解,应该下面的最优解也是1,但是这里实际上是3。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值