零一背包_DP二维数组及优化及DP一维数组

4 篇文章 0 订阅
3 篇文章 0 订阅

零一背包

题目描述

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

DP_二维数组代码

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

const int N = 1001;
int n,m;
int v[N],w[N],f[N][N]={0};

int main()
{
    cin >> n>> m;
    for(int i=1;i<=n;i++)
        cin >> v[i] >> w[i];

	// f[i][j] 表示从前 i 个物品里面选择体积不超过 j 的最大价值
	for(int i=1;i<=n;i++)
    	for(int j=0;j<=m;j++){
        	// 不选择第 i 个物品
        	f[i][j] = f[i-1][j];
        	// 选择第 i 个物品
        	if(j >= v[i])
            	f[i][j] = max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
    	}
	int res = 0;
	for(int i=0;i<=m;i++)
    	res = max(res,f[n][i]);
	cout << res << endl;
	return 0;
}

注意:其中,核心代码部分

f[i][j] = max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
// 等价于:
f[i][j] = max(f[i][j],f[i-1][j-v[i]]+w[i]);

优化: 由于核心代码部分的两层循环中,内层循环里面要更新f [i] [j] ,只与上一层的 f [i-1] [j]有关,所以不用记录整一层。优化方法:只采用一维数组(另一种优化方式:滚动数组)。

DP_一维数组代码

for(int i=1;i<=n;i++)
    	for(int j=v[i];j<=m;j++){
        	// 1. f[i][j] = f[i-1][j]; 由于是一维的,暂且视作只与j有关,所以与第一层没有关系,这一行可以直接注释
        	// 3. 直接将判断条件转移到 for 循环里
        	if(j >= v[i]){
        	 //f[i][j] = max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
        		// 2. 尽量做等价变换
        		// 如果直接改为 f[j] = max(f[[j],f[j-v[i]]+w[i]);
        		// 其实等价于 f[i][j] = max(f[i][j],f[i][j-v[i]]+w[i]);
        		// 而上面提到了 f[i][j] = max(f[i-1][j],f[i-1][j-v[i]]+w[i]) 等价于...max(f[i][j],...)
        		// 所以 式子中只有 max 函数里的第二项 f[j-v[i]]+w[i] 不是等价变换的
        		// 想要做等价变换,现在就需要想办法让 f[j-v[i]] 代表原来的 f[i-1][j-v[i]] 而不是 f[i][j-v[i]]
        		/* 实现思路:内层循环用逆序,才能使得 f[j-v[i]]是上一层没有被更新的(j-v[i]肯定小于j,
        		而内层循环 逆序 表明 f[j] 肯定比 f[j-v[i]] 要先被修改,故f[j-v[i]]是未被修改的值,
        		也就是说,f[j-v[i]]是上一层的值,即等价于原来的f[i-1][j-v[i]])*/
        	}
    	}

优化后的最终代码

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

const int N = 1001;
int n,m;
int v[N],w[N],f[N][N]={0};

int main()
{
    cin >> n>> m;
    for(int i=1;i<=n;i++)
        cin >> v[i] >> w[i];

	for(int i=1;i<=n;i++)
    	for(int j=v[i];j>=m;j--)
            f[j] = max(f[j],f[j-v[i]]+w[i]);
	cout << f[m] << endl;
	return 0;
}

说明: 核心代码部分,按照上述代码注释中的1,2,3步骤进行更改,最终结果即为优化结果

步骤2:尽量做等价变换

 1. 如果直接改为 f[j] = max(f[[j],f[j-v[i]]+w[i]);
 2. 其实等价于 f[i][j] = max(f[i][j],f[i][j-v[i]]+w[i]);
 3. 而上面提到了 f[i][j] = max(f[i-1][j],f[i-1][j-v[i]]+w[i]) 等价于...max(f[i][j],...)
 4. 所以 式子中只有 max 函数里的第二项 f[j-v[i]]+w[i] 不是等价变换的
 5. 想要做等价变换,现在就需要想办法让 f[j-v[i]] 代表
 原来的 f[i-1][j-v[i]] 而不是 f[i][j-v[i]]
 6. 实现思路:内层循环用逆序,才能使得 f[j-v[i]]是上一层没有被
 更新的(j-v[i]肯定小于j,而内层循环 逆序 表明 f[j] 肯定
 比 f[j-v[i]] 要先被修改,故f[j-v[i]]是未被修改的值,也就是说,
 f[j-v[i]]是上一层的值,即等价于原来的f [i-1] [j-v[i]])

参考零一背包_一维数组

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值