01背包(背包入门精讲)

01背包(题目加代码)

题目描述

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

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

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

输入描述

第一行两个整数,n,m,用空格隔开,分别表示物品数量和背包容积。

接下来有 n 行,每行两个整数 vi​,wi​,用空格隔开,分别表示第 i 件物品的体积和价值。
数据范围:
0<n,m≤1000
0<vi​,wi​≤1000

输出描述

输出一个整数,表示最大价值。

样例输入 1 

4 5
1 2
2 4
3 4
4 5

样例输出 1 

8
一.二维数组

动态规划的核心就是找到原问题与子问题的关系,并列出动态转移方程。

分析:
这里我们可以定义一个二维数组,dp[i][j]表示对于背包容量为j的背包,前i个物品的最优解,即最大价值。
对于一个物品,我们可以分为以下两种情况:

不选:对于dp[i][j],不选第i个物品时,dp[i][j]的最优解就是dp[i-1][j]的最优解
选:如果选择,我们就让背包容量减去第i件的物品体积,让dp加上物品价值,即dp[i][j]=dp[i-1][j-v[i]]+w[i];

这样我们就得到了状态转移方程,如果要计算对于前n个物品背包容量为V的背包的最优解,只需要一层一层往前推,通过前面的子问题,求得最终答案。

状态转移方程:dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);

代码:

#include<bits/stdc++.h>
using namespace std;
int dp[1010][1010];
int v[1010],w[1010];//体积和价值
int main(){
	int n,V;
	int i,j;
	cin>>n>>V;//商品个数和背包容量
	for(i=1;i<=n;i++){
		cin>>v[i]>>w[i];//体积和价值
	}
	for(i=1;i<=n;i++){//依次遍历从第1个物品到底n个物品
		for(j=1;j<=V;j++){//依次遍历从0~背包容量v
			if(j<v[i]){//如果背包容量小于物品体积
				dp[i][j]=dp[i-1][j];//最优解就是上一个物品时的最优解
			}else{//否则就是背包容量大于等于物品体积
				dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);//拿或者不拿,选最优,同时注意是与上一个i-1比较
			}
		}
	}
	cout<<dp[n][V]<<endl;//输出前n个商品背包为m的最优解
    system("pause");
	return 0;
}
二.一维数组优化(防止上面二维时间超限)

从空间优化考虑,在上面的二维数组中我们可以发现有很多重复的数据,我们只要得到第i-1层的结果,就能得到答案,对于前面的数据我们完全没必要储存,所以我们可以定义一个一维数组dp[i]表示背包容量为i时的最优解。我们就可以得到一个状态转移方程:

dp[j]=max(dp[j],dp[j-v[i]]+w[i]);

第一个for循环遍历从1~n的物品。第二个for倒序从V到1遍历。这里强调一下,必须是倒序遍历,倒序遍历保证了对于dp[j],他要调用的dp[j-v[i]]一定是第i-1更新过的,第i层还没有进行更新。

好了,话不多说,上代码: 

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int dp[N];
int v[N],w[N];//体积和价值
int main(){
	int n,V;//商品个数和体积
	int i,j;
	cin>>n>>V;//商品个数和背包容量
	for(i=1;i<=n;i++){
		cin>>v[i]>>w[i];//体积和价值
	}
	for(i=1;i<=n;i++){//依次遍历从第1个物品到底n个物品
		for(j=V;j>=v[i];j--){
			dp[j]=max(dp[j],dp[j-v[i]]+w[i]);//一维数组从最后往前面遍历
		}
	}
	cout<<dp[V]<<endl;//输出背包为V的最优解
	return 0;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值