背包问题是一个np问题:
一般题意为:
存在n件物品,每件物品的重量为w[i],价值为v[i],现在有一个包,承重限制为weight,现在让你从n件物品中,选择一些物品装入背包中,在不超重的前提下,使得背包中所装物品的总价值最大。
由于每件物品【应该是每种物品】存在着两种情况,一种是,每种物品唯有一件,这种情况称为0-1背包问题;
另一种是,每种物品有无数件,这种情况称为完全背包问题。
0-1背包问题:
0-1背包问题存在两种情况,即,对于第i种物品,你只有选择或者不选择两种情况。
(1)使用动态规划来解答:
考虑对第i件物品的选择策略,有两种策略:
①不放第i件物品,那么问题转化为前i-1件物品恰好装入容量为weight的背包中所能获得的最大价值,也即dp[i-1][weight]。【也就是整个背包重量用来装前i-1件物品】
②放第i件物品,那么问题转化为前i-1件物品恰好装入容量为weight-w[i]的背包中所能获得的最大价值,也即dp[i-1][weight-w[i]]+c[i]。【也就是前i-1件物品只能装到重量的v-w[i]】
即选择物品和不选择物品这两种情况而已【weight为当前剩余的背包体积】
状态转移方程:
dp[i][u] = max{dp[i-1][u], dp[i-1][u-w[i]] + v[i]} (1<=i<=n. w[i]<=u<=weight)
核心代码为:
1 for(int i=1; i<n;++i) //从1开始,0为初始边界 2 for(int u=w[i];u<=weight;++v)//正序枚举u 3 dp[i][u] = max(dp[i-1][u], dp[i-1][u-w[i]] + c[i]);
当然,为了缩小空间复杂度,我们也可以用一维数组求解,不过,u的遍历就要逆序遍历了
状态转移方程:
dp[i][u] = max(dp[u], dp[u-w[i]]+v[i]); 1<=i<=n, w[i]<=v<=weight
1 for(int i=0; i<=n; ++i) 2 for(int u=weight; u>=w[i]; --u)//逆序枚举 3 dp[i][u] = max(dp[u], dp[u-w[i]]+v[i]);
(2)使用DFS来求解:
很简单,即分两条路进行递归
1 void DFS(int index, int sumW, int sumV) 2 { 3 if(sumW>weight) 4 return; 5 maxV = max(maxV, sumV); 6 for(int i=index; i<n; ++i) 7 { 8 DFS(index+1, sumW, sumV);//不选择index 9 DFS(index+1, sumW+w[index], sumV+v[index]);//选择index 10 } 11 }
完全背包问题:
(1)使用动态规划求解
二维数组:
状态转移方程:
dp[i][u] = max{dp[i-1][u], dp[i][u-w[i]] + v[i]}; 1<=i<=n, w[i]<=u<=weight
边界:
dp[0][u] = 0 0<=v<=u
1 for(int i=1; i<n;++i) //从1开始,0为初始边界 2 for(int u=w[i];u<=weight;++v)//正序枚举u 3 dp[i][u] = max(dp[i-1][u], dp[i][u-w[i]] + c[i]);
一维数组:
状态转移方程:
dp[u] = max{dp[u], dp[u-w[i]] + v[i]} 1<=i<=n, w[i]<=u<=weight
边界:
dp[u] = 0 0<=u<=weight
1 for(int i=1; i<=n; ++i) 2 for(int u=weight; u>=w[i]; --u) 3 dp[u] = max(dp[u], dp[u-w[i]]+v[i])
使用DFS求解
1 void DFS(int index, int sumV, int sumW) 2 { 3 if (sumW > weight) 4 return; 5 if (sumV> maxV) 6 maxV = sumV; 7 for (int i = index; i < n; ++i) 8 { 9 DFS(i + 1, sumV + v[index], sumW + w[index]); //选这件物品 然后继续选这一件 10 DFS(i + 1, sumV + v[index], sumW + w[index]); //选这件物品 然后选下一件 11 DFS(i + 1, sumV, sumW); //不选这件物品 12 } 13 }