背包理论基础
每个物品只有两种状态,取或不取,所以可以使用回溯法搜出所有的情况,那么复杂度为O(2^n),这里n表示物品数量。用动态规划进行优化。
二维dp数组01背包
1.dp[i][j]表示从标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
(j表示背包最大可以承受的重量,也就是背包容量)
2.确定递推公式
不放物品i:有两个方向推出dp[i][j],即背包容量为j,里面不放物品i的最大价值,此时dp[i][j]就是dp[i - 1][j]。(其实就是当物品i的重量大于背包j的重量时,物品i无法放进背包中,所以背包内的价值依然和前面相同。)
放物品i:由dp[i - 1][j - wight[i]]推出,dp[i - 1][j - wight[i]]为背包容量为j - wight[i]的时候不放物品i的最大价值,(最大价值依然还和上次i-1处一样,没变),那么dp[i - 1][j - weight[i]] + value[i](物品i的价值),就是背包放物品i得到的最大价值(背包里没能放进去当前这一件物品,因此背包价值和上一次一样,但是最终价值=当前这个背包的价值(可能含有前面放进的物品)+当前没能放进去的物品的价值)。所以递归公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
放或不放这个物品的到的价值进行比较,取其中较大的那个。
3.初始化
首先从dp[i][j]定义出发,如果背包容量j为0的话,即dp[i][0],无论是选取哪些物品,背包价值总和一定为0.
状态转移方程dp[i][j] = max(dp[i - 1][j],dp[i - 1][j - weight[i]] + value[i]);可以看出i是由 i -1推导得出,那么i 为0的时候必须进行初始化。
dp[0][j],即i为0,存放编号为0的物品的时候,各个容量的背包所能存放的最大价值。
那么很明显当j < weight[0]的时候,dp[0][j]应该是0,因为背包容量比编号0的物品重量还小。
(背包本身价值为0,或者说背包当前的价值,取决于放进去物品的总价值,如果当前物品没放进去,就只受到前面放进去的物品总价值的影响,如果放进去了,就受下标为0到i的所有物品的价值的影响。)
当j >= weight[0]时,dp[0][j]应该是value[0],因为背包容量足够放下编号为0的物品。
for(int j = 0 ; j < weight[0]; j++){
dp[0][j] = 0;
}
for(int j = weight[0]; j <= bagweight; j++){
dp[0][j] = value[0];
}
由递推公式dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])可以知道,dp[i][j]是从左上方的数值推导得出来的,那么其他下标是什么数值都可以,因为后面会被覆盖。
统一初始化为0,更方便。
vector<vector<int>>dp(weight.size(),vector<int>(bagweight + 1,0));
for(int j = weight[0]; j <= bagweight; j++){
dp[0][j] = value[0];
}
i-1时未放入第i件物品,此时对应的背包容量未j-weight[i],,,,此处应知道,背包容量未j时对应放进的物品为i。在这里我们先考虑前 i - 1件物品放入背包的最优解,再考虑第i件。
能不能放的是前提条件是,背包容量j与物品i的重量weight[i],
背包大小只要是大于等于物品重量,就可以放,但第i件物品要不要放,还要从第i - 1件物品为起点进行考虑。
最终决定要不要放的,才是价值的大小。
if(j < weight[i])dp[i][j] = dp[i - 1][j];
else dp[i][j] = max(dp[i - 1][j],dp[i - 1][j - weight[i] + value[i]);
//这里在判断背包大小能够容纳第i件物品后,又比较了不放和放第i件物品的价值大小,取大的为结果。