推荐背包九讲,非常棒。以下是个人对其前四讲内容的梳理和Java实现,用于快速回顾知识点。
01背包
问题:
N件物品,一个容量M的背包。A[]是物品体积,V[]是物品价值,求背包能装物品的最大价值。
思路:
dp[i][j]表前i件物品在容量最大为j的限制下能得到的最大价值。
public int pack01Solution1(int m,int A[],int V[] ){
int[][] dp = new int[A.length+1][m+1];
for(int i=0;i<A.length;i++){
for(int j=0;j<=m;j++){
if(A[i]>j)dp[i+1][j] = dp[i][j];
else dp[i+1][j] = Math.max(dp[i][j],dp[i][j-A[i]]+V[i]);
}
}
return dp[A.length][m];
}
时间复杂度O(NM),空间复杂度O(NM)。
空间优化为O(M):
public int pack01Solution2(int m,int A[],int V[] ){
int[]dp = new int[m+1];
for(int i=0;i<A.length;i++){
for(int j=m;j>=A[i];j--){
dp[j] = Math.max(dp[j],dp[j-A[i]]+V[i]);
}
}
return dp[m];
}
可以把01背包中对一件物品的处理抽取为方法:
public void ZeroOnePack(int cost,int value,int[] dp,int m){
for(int j=m;j>=cost;j--)
dp[j] = Math.max(dp[j],dp[j-cost]+value);
}
初始化的细节问题:
完全背包问题
问题:
N种物品,背包容量为M,每种无限件,给出物品体积和价值A[]、V[],求最大价值。
思路:
dp[i][j] = max{dp[i-1][j-k*A[i]]+k*V[i]|0<=k*A[i]<=M}
时间复杂度:有O(NM)个状态,每个状态求解时间O(M/A[i]),总时间O(Σ(M/A[i])*M).
转化为01背包问题:
-第i种物品转化为M/A[i]件体积和价值不变的物品,复杂度不变
-第i种物品表示成2^k件物品的总和,A[i]*(2^k)<=M,每种物品可拆成O(logM/A[i])件物品
-一维数组实现的**O(NM)**算法:dp[i][j] = max{dp[i-1][j],dp[i][j-A[i]]+V[j]}
public int packComSolution(int m,int[] A,int[] V){
int[] dp = new int[m+1];
for(int i=0;i<A.length;i++){
for(int j=A[i];j<=m;j++)
dp[j] = Math.max(dp[j],dp[j-A[i]]+V[i]);
}
return dp[m];
}
其中处理一种完全背包的物品可以抽取方法:
public void CompletePack(int cost,int value,int[] dp,int m){
for(int j=cost;j<=m;j--)
dp[j] = Math.max(dp[j],dp[j-cost]+value);
}
多重背包
问题:
在01背包基础上,增加条件每件物品的可取件数n[];
思路:
转化成01背包,每种物品的n[i]都可以用2^0,2^1,..2^(k-1),n-(2^k-1)中若干数的和表示,k是满足n[i]-2^k+1>0的最大整数。
第i种物品分成了O(log n[i])种物品,复杂度从O(M*Σn[i])变为O(M* Σlog n[i])
public void packMutiSolutionk(int m,int[] A,int[] V ,int[] N){
int[] dp = new int[m+1];
for(int i=0;i<A.length;i++){
MutiPack(A[i],V[i],N[i],dp,m);
}
}
public void MutiPack(int cost, int value, int amount, int[] dp, int m){
if(cost*amount>=m){
CompletePack(cost,value,dp,m);
return;
}
int s=1;
while(s<amount){//条件:amount-s>0
ZeroOnePack(s*cost,s*value,dp,m);
amount -= s;
s *= 2;
}
ZeroOnePack(amount*cost,amount*value,dp,m);
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。