动态规划
***原文声明:本文是本人根据宫水三叶的博文整理的随笔,关注原文请移步至:宫水三叶
背包问题
**0-1背包问题:**题目描述:有N件物品和一个容量为V的背包。每件物品有且只有1件。第i件物品的体积是v[i],价值是W[i]。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
1、dp[N][C+1]
解法:
对于着了问题,如果设计DFS(int []v,int []w,int i,int c);其中v 和w 是不变参数不必关心。i和c分别表示当前枚举到哪件物品和现在剩余的容量。返回值为最大价值。
定义状态:dp[i][c]
考虑前i件物品且容量不超过C的最大价值。
不选:其实就是dp[i-1][c]
选择:dp[i-1][c-v[i]]+w[i]
前提是当前剩余容量>=物品的体积。
状态转移方程:dp[i][c]=max(dp[i-1][c],dp[i-1][c-v[i]]+w[i])
class Solution{
public int maxValue(int N,int C,int []v,int []w){
int [][]dp=new int[N][C+1];
//第一件
for(int i=0;i<=C;i++){
dp[0][i]=i>=v[0]?w[0]:0;
}
for(int i=1;i<N;i++){
for(int j=0;j<C+1;j++){
//不选该物品
int n=dp[i-1][j];
//选择
int y=j>=v[i]?dp[i-1][j-v[i]]+w[i]:0;
dp[i][j]=Math.max(n,y);
}
}
return dp[N-1][C];
}
}
优化1:由于计算第i行格子,只需要第i-1行格子的数据,因此可以使用dp[2][C+1]
滚动数组,使用维度的地方进行i&1或者i%2运算即可。
优化2:计算第i行格子时,不仅只依赖于i-1行,而且只依赖于i-1行的第c个格子和c-v[i]个格子。因此我们将求解第i行格子的顺序【从0到c]改为【从c到0】。可以将原来的二维数组压缩到一行。
class Solution{
public int maxValue(int N,int C,int[]v,int []w){
int []dp=new int[C+1];
for(int i=0;i<N;i++){
for(int j=C;j>=V[i];j--){
//不选
int n=dp[j];
//选择
int y=dp[j-v[i]]+w[i];
dp[j]=Math.max(n,y);
}
}
return dp[C];
}
}
**背包问题:**在0-1背包的基础上,物品的有无限件,可以多次选择。
因此,dp[i][j]
表示考虑前i件物品放入容量为j的背包,获得最大的价值。由于物品可以多次选择,因此dp[i][j]
就是[0-k]件,dp[i-1][j-k*v[i]]+k*w[i]
的最大值。
状态转移方程:
dp[i][j]=max(dp[i-1][j],dp[i-1][j-k*v[i]]+k*w[i]),0<k*v[i]<=j
class solution{
public int maxValue(int N,int C,int[]v,int []w){
int dp[][]=new int[N][C+1];
for(int j=0;j<=C;j++){
int maxk=j/v[0];
dp[0][j]=maxK*w[0];
}
for(int i=1;i<N;i++){
for(int j=0;j<=C;j++){
int n=dp[i-1][j];
int y=0;
for(int k=1;;k++){
if(j<v[i]*k)break;
}
y=Math.max(y,dp[i-1][j-k*v[i]]+k*w[i]);
}
dp[i][j]=Math.max(n,y);
}
}
return dp[N-1][C];
}
//时间复杂度O(N*C*C),空间复杂度O(C*N)
一维空间优化:
dp[i][j]=dp[i-1][j-k*v[i]]+k*w[i],0<k*v[i]<=j
dp[i][j-v[i]]=dp[i-1][j-(k-1)*v[i]]+k*w[i],0<k*v[i]<=j
对应于dp[i][j]和d[i][j-v[i]]
,两式具有部分等差性,即部分对应总是相差w[i]。
因而dp[i][j]
最大值的问题转化为dp[i][j-v[i]]+w[i]
的最大值。==>dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i])
消除i的维度,dp[j]=max(dp[j],dp[j-v[i]]+w[i])
所以计算dp[j]时需要保证dp[j-v[i]]
已经保存好。因此将j的遍历顺序改为从小到大即可。以保证使用dp[j-v[i]]
计算dp[j]的时候,dp[j-v[i]]的值不会更新。
class solution{
public int maxValue(int N,int C,int[]v,int []w){
int []dp=new int[C+1];
for(int i=0;i<N;i++){
for(int j=0;j<=C;j++){
int n=dp[j];
int y=j-v[i]>=0?dp[j-v[i]]+w[i]:0;
dp[i][j]=Math.max(n,y);
}
}
return dp[C];
}
}
//时间复杂度O(N*C),空间复杂度O(C)