其实背包问题大一的时候C++老师就一直念叨,但是奈何懒和无知,老师讲的时候就总觉得soeasy。
老师原话解释如下:你的背包容积为M,然后学校某社团搞活动发零食(见者有份)一共有N种零食,每种每人只能拿一包,第i种体积为w[i],好吃程度为d[i],你要怎么拿才能拿到有多又好吃的?当然是一包一包的选择拿或者不拿,然后再比较拿了或者不拿的好吃程度(在背包容积范围内)最后就可以了呀。
当时就觉得:哦!不就是做个比较吗?然后现在遇到了相似问题才发现我真的太无知了。好了,废话到此结束。接下来就是刚刚看网课学到的笔记了。
--------------------------------分割线---------------------------------
有N件商品,购物车容积为M,第i件商品的体积为w[i],价值为d[i],将哪些商品装入购物车可以使价值总和最大,每件商品有仅有一件,可选择拿或者不拿。数据如下:
(图片来自中国大学mooc北航《算法设计与分析》)
首先我们可以设置一个二维数组 F[i][j] 表示取前i种商品,使总体积不大于j,总价值为F[i][j]。
如果不取第i件商品,则总价值为F[i-1][j];
如果取第i件商品,则总价值为(F[i-1][j-w[i]]+d[i]);
取两者最优就是取两者价值最大的,所以可以推出递推式:
F[i][j] = max( F[i-1][j] , F[i-1][j-w[i]]+d[i])
但是这样开二维数组容易超内存,可以采用滚动数组的方式,把二维数组优化为一维数组F[v],v表示背包容积。因为新求出的F[v]的值会覆盖F[v+1]的值,所以 v 要从M开始递减。
优化后如果不取第i件商品,总价值为F[v];
如果取第i件商品,则总价值为F[v-w[i]]+d[i];
可以得到递推式为:
F[v]=max(F[v],F[v-w[i]]+d[i])
完整代码如下(求解图片上的问题):
#include <iostream>
#include <string>
#include <algorithm>
#define N 100000
using namespace std;
int main()
{
int n,m,w[N],d[N],f[N];//共有n件物品背包体积m,体积数组w[],价值数组d[]
cin>>n>>m;
int i,v;
for(i=1;i<=n;i++)
cin>>d[i]>>w[i];
for(i=1;i<=n;i++)
for(v=m;v>=w[i];--v)
f[v]=max(f[v],f[v-w[i]]+d[i]);
cout<<f[m]<<endl;//此时最优解是f[m]不是f[v]
/*可以用输出f[]的方式检验最优解为f[m]
for(i=1;i<=m;i++)
{
cout<<f[i]<<endl;
}
*/
return 0;
}
通过蛮力枚举可以得到最优解:
(图片来自中国大学mooc北航《算法设计与分析》)
运行结果如下:
其实也并没有理解的很透彻,所有要是有解释不当的地方,还请各位大佬指出来,谢谢啦。