题目链接
题目描述
已知一个背包最多能容纳物体的体积为V
现有n个物品第i个物品的体积为v_iv
i
第i个物品的重量为w_iw
i
求当前背包最多能装多大重量的物品
示例1
输入
10,2,[[1,3],[10,4]]
输出
4
说明
第一个物品的体积为1,重量为3,第二个物品的体积为10,重量为4。只取第二个物品可以达到最优方案,取物重量为4
备注:
1≤V≤200
1≤n≤200
1≤v≤200
1≤w≤200
解题思路
对于动态规划问题,我们首先需要搞清楚【状态】和【选择】是什么?
这道题涉及到的变化的状态量有两个,就是背包的体积和当前可选的物品,选择就是把物体装进背包或者不装进背包。
这样就可以定义我们的dp数组了.
1. 定义dp数组
首先两个状态量就是二维dp数组的两个维度。
dp[i][j]=x表示对于前i个物体,当前背包的体积为j,这种情况下可以装的最大重量是dp[i][j]
这里的定义可能并不直观,但是这是背包问题的典型套路,记住就行了
2. 确定base case
物体个数为0或者背包体积为0时可以装的物体重量都为0,即
dp[0][...]=0,dp[...][0]=0
3. 推导状态转移方程
推导状态转移方程本质就是做选择,也就是把第i个物体放入或者不放入背包,然后做最优选择就行了
dp[i][j] = max(dp[i-1][j], dp[i-1][j-vw[i-1][0]] + vw[i-1][1])
这里需要注意的是数组的下标,由于i是从1开始的,所以题目中的给的数组下标为i-1时表示的是第i个物品的重量。
题解
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 计算01背包问题的结果
* @param V int整型 背包的体积
* @param n int整型 物品的个数
* @param vw int整型二维数组 第一维度为n,第二维度为2的二维数组,vw[i][0],vw[i][1]分别描述i+1个物品的vi,wi
* @return int整型
*/
func knapsack(V int ,n int ,vw [][]int ) int {
// 定义dp数组,dp[i][j]表示对于前i个物体,当前背包体积为j,最多可以装多大重量的物体
dp := make([][]int, n+1)
for i := 0; i <= n ; i++ {
// 定义base case
// 物体个数为0或者背包体积为0时可以装的物体重量都为0,即
// dp[0][...]=0,dp[...][0]=0这个已经自动初始化好了
dp[i] = make([]int, V+1)
}
// 推导状态转移方程
// 按照物体数量进行遍历
for i := 1; i <= n; i++ {
// 按照背包可用体积进行遍历
for j := 1; j <= V; j++ {
// 背包体积已经不够装第i个物体了,只能不装入背包
if j - vw[i-1][0] < 0 {
dp[i][j] = dp[i-1][j]
} else {
// 可以选择装入或不装入,取较大值
dp[i][j] = max(dp[i-1][j], dp[i-1][j-vw[i-1][0]] + vw[i-1][1])
}
}
}
return dp[n][V]
}
复杂度分析
时间复杂度:O(C*N)
假设物品的个数为N, 背包的容量是C,两层循环,所以算法的总时间复杂度是 O(C*N)
空间复杂度:O(C*N)
需要开辟一个为C*N大小的dp数组。
反思
背包问题是非常经典的动态规划问题,这是最基本的问题,先熟悉一下套路。