贪心可行吗?
关于这个问题 各位同学可以思考一下!
如果用贪心的思想每次选取最大的放入背包
那么可以试试以下这个数据
物品编号 | 体积 | 价值 |
1 | 5 | 600 |
2 | 2 | 250 |
3 | 3 | 450 |
若背包容量为5
那么我们第一次选取最大的物品1,获得的价值为600 此时背包已经装满
但是此时是否为装满背包获取的最大价值呢?
答案是否定的!!!
因为我们选取物品2+物品3 体积占用为5 价值为250+450=700
显然是比第一种情况是价值更大
因此这类问题我们不可以用贪心来思考
于是便引入了我们今天的模型01背包
模拟大法
因为贪心的思想是行不通的,所以我们可以进行模拟所有情况看一下能不能寻找出什么规律
这里第一行是一个物品都不选的情况那么无论分配多少体积 他的价值都为0
第二行选取物品1,当体积为5的时候,而体积0-4因为无法装满所以价值为0
(这里为何要讨论0-4这种情况呢,那是因为我们实际的数据是不确定的,我们讨论要遵循不重不漏的原则,虽然这里显得多余,但是有的数据就需要这种讨论,比如如果有一个物品体积为1价值为200 那么在体积为1的时候他就可以被装入)
再来看物品2,物品2的体积为2,因此当体积为2的时候才可以装入,价值为250,后面2-4一致
然而当背包的体积为5的时候,情况就发生了改变
此时我们需要讨论两种情况
1 不选物品2 那么我们就选取物品1 价值为600
2 选取物品2 那么物品1就没法选了,因为物品2需要占用体积2 剩余体积只有3 物品1无法放入背包 此时价值为250
对1和2选取一个最大的情况 因此当需要在前两个物品中进行选择的时候我们选取物品1放入背包,获取的最大价值为600
然后我们看从前3个物品中进行选择
当背包体积为0-1时候,任何物品都没法放入背包 背包价值为0
当背包体积为2的时候,我们可以选择物品2 这时背包价值为250
当背包体积为3的时候 我们面临两种选择
1 不选第3个物品 从前两个物品中进行选择,那么我们的最大价值就是上一行的价值从前两个物品中选,价值最大为250
2选取第3个物品 先给第三个物品预留空间 ,此时背包3-3=0没有多余空间,那么只能放物品3 最大价值为450
背包体积为4时同理
当背包体积为5时,我们有两种选法
1不选第3个物品 即从前两个物品中选择 背包体积为5的时候的最大价值
2选第3个物品 那么先给第三个物品预留空间,那么还剩下两个空间,再从前两个中选择,体积不超过2,由上行数据我们可以看出,最大价值为250,250+450=700 从而得出最大价值
也就得出了从3个物品中选,体积为5时的最大价值
递推公式
我们可以发现上面
每个物品 只有选和不选两种情况
选第i个物品:就是先预留空间j-v dp[i-1][j-v]就表示前i-1个物品中选体积不超过j-v的最大价值 然后再加上物品i的价值
不选物品i:就是直接从前i-1个物品中选体积不超过j
而我们要求就是再背包体积为j的情况下能获得的最大值
我们可以设置一个二维数组dp[n][m]
dp[i][j]表示从前i个物品中选,体积不超过j的最大价值
根据上述我们可以得出 dp[i][j]=max(dp[i-1][j],dp[i-1][j-v]+w)
因为每次更新都只有选和不选两种情况
关于初始化的问题
我们物品编号从1开始 所以当dp[0][j]表示一个物品都不选体积为j时的最大价值 都一个物品没选了 那价值肯定为0 这是第0行的数据 所以默认为0
然后就是dp[i][0]表示前i个物品中选 背包体积为0时的最大价值 背包体积为0 一个都装不下 所以dp[i][0]也都是0
然后我们下面的代码读入i和j都是从1开始读入 又因为我们dp[i][j]开在全局变量默认为0 所以此时不用初始化 或者说已经初始化完毕
代码
#include <iostream>
using namespace std;
int v[1010],w[1010];
int f[1010][1010];//这里开成全局变量 所以默认为0 f[0][0-m]表示一个物品都不选 总体积不超过0-m 因为一个都没选所以默认为0
//
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>v[i]>>w[i];//读入物品的体积和价值
//从1开始 不用初始化
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(v[i]>j)f[i][j]=f[i-1][j];//如果当前物品比体积还要大 那么就不选该物品 //选的时候可能不存在
//此时等同于从前i-1中选体积不超过j
else
f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
//否则就是选与不选两者取最大值
}
}
cout<<f[n][m];
return 0;
}