题目
描述
有 n 个物品和一个大小为 m 的背包. 给定数组 A 表示每个物品的大小和数组 V 表示每个物品的价值.
问最多能装入背包的总价值是多大?
1.A[i], V[i], n, m 均为整数
2.你不能将物品进行切分
3.你所挑选的要装入背包的物品的总大小不能超过 m
4.每个物品只能取一次
5.m <= 1000m<=1000
len(A),len(V)<=100len(A),len(V)<=100
样例
样例 1:
输入:
m = 10
A = [2, 3, 5, 7]
V = [1, 5, 2, 4]
输出:
9
解释:
装入 A[1] 和 A[3] 可以得到最大价值, V[1] + V[3] = 9
样例 2:
输入:
m = 10
A = [2, 3, 8]
V = [2, 5, 8]
输出:
10
解释:
装入 A[0] 和 A[2] 可以得到最大价值, V[0] + V[2] = 10
分析
题目中指定每个物品只能拿一次且不能将物品分割,所以这道题是一道01背包动态规划
首先要确定最后一步
f[n],[m] (n是物品的数量,m为背包的容量),它的前一个状态f[n-1],[m]就是拿n-1个物品的状态,而最后一步只有两个选择,如果f[n-1],[m]已经达到了最大值,那最后一件物品就不需要拿了,反之就需要拿。
转移方程
f[n],[m]=max(f[n-1],[m] f[n-1],[m-最后一件物品]+最后一件物品的价值 )条件:m>=物品重量&&f[n-1],[m-最后一件物品]可以拼的出来
开始写代码
1.处理初始化
创建表示状态的二维数组
vector<vector<int> > f(len+1,vector<int>(m+1));
初始化数组的第一个元素以及第一行,第一个0,0的状态是0件物品要求容量0,那这个状态的值就是0,然后就是(0,1),(0,2)…(0,m)分别代表物品为0个时,拼出重量为1,2,3…m的最大价值,当然是拼不出来,这里用-1来表示拼不出来
f[0][0]=0; //当拿0个物品时
for(int i=1;i<=m;i++)
f[0][i]=-1;
2.转移方程
接下来就是核心,转移方程创建二层循环,外层的循环代表物品的数量,内层代表背包的容量,值代表最大价值,-1代表拼不出来当前重量
for(int i=1;i<=len;i++) //动规核心
{
for(int j=0;j<=m;j++)
{
f[i][j]=f[i-1][j]; //初始化
if(j>=A[i-1]&&f[i-1][j-A[i-1]]!=-1)
{
f[i][j]=max(f[i][j],f[i-1][j-A[i-1]]+V[i-1]);
}
}
}
3.找出最大价值并返回
这个时候最可能犯的错误就是直接返回f[n],[m],但是背包装的重量最大不一定价值就是最大的,所以应该在最后一行中找出一个最大值
int ans=0;
for(int i=0;i<=m;i++)
ans=max(ans,f[len][i]);
完整代码
class Solution {
public:
int backPackII(int m, vector<int> &A, vector<int> &V) {
int len=A.size();
vector<vector<int> > f(len+1,vector<int>(m+1));
f[0][0]=0; //当拿0个物品时
for(int i=1;i<=m;i++)
f[0][i]=-1;
for(int i=1;i<=len;i++) //动规核心
{
for(int j=0;j<=m;j++)
{
f[i][j]=f[i-1][j]; //初始化
if(j>=A[i-1]&&f[i-1][j-A[i-1]]!=-1)
{
f[i][j]=max(f[i][j],f[i-1][j-A[i-1]]+V[i-1]);
}
}
}
int ans=0;
for(int i=0;i<=m;i++)
ans=max(ans,f[len][i]);
return ans;
}
};