很经典的问题啦,来讲讲我自己的思路
题目如下:
(别一看到旅行者就原神启动......玩原神玩的)
咳咳,步入正题,这题要用到动态规划,后面遇到的题如果有“最大最下”可能就是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。