ACM算法_背包小结
01背包,完全背包,多重背包
模板
背包的基本模型就是给你一个容量为V的背包,在一定的限制条件下放进最多(最少?)价值的东西
01背包
01背包的基本模型就是有N件物品和一个容量为W的背包。(每种物品均只有一件)第i件物品的占重是w[i],价值是v[i]。求解将哪些物品装入背包可使价值总和最大。 特点:每种物品仅有一件,可以选择放或不放。 动态转移方程: - dp[i][j]表示前i件物品恰放入一个容量为j的背包可以获得的最大价值。 - dp[i-1][j]表示前i-1件物品恰放入一个容量为j的背包可以获得的最大价值。也就是,当前遍历到的物品不加入背包。 - dp[i-1][j-w[i]]+v[i]表示前i-1件物品恰放入一个容量为(j-当前遍历到的物品重量)的背包可以获得的最大价值。也就是将当前遍历到的物品加入背包。至于把谁挤下去,在便利的过程中已经实现。 dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i])
代码实现
直接用二维数组dp[maxn][maxm];
关键代码
for(int i=1;i<=n;++i){//遍历物品
for(int j=w[i];j<=W;++j){//遍历背包容量(因为动态转移方程里有对j-w[i]的访问,所以j从w[i]开始)
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
}
}
优化策略:由上可知,二维数组并不会交叉重复访问,因此可将二位数组优化为一维数组,将每次访问时变量的值当作i-1项的值,第二层遍历背包时,采用逆序,可保证,更新第i层的dp[j]前,dp[j]和dp[j-w[i]]储存的是第i-1层的值。
关键代码
for(int i=1;i<=n;++i){//遍历物品
for(int j=W;j>=w[i];--j){//遍历背包容量(因为动态转移方程里有对j-w[i]的访问,所以j从w[i]开始)
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
完全背包
完全背包的基本模型就是有N种物品和一个容量为W的背包,每种物品都有无限件可用。第i种物品的占重是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。 动态转移方程: dp[i][j]=max(dp[i][j],f[i-1][j-k*w[i]]+k*v[i]) 与01背包略有不同,完全背包是与当前背包状态比较的
代码实现
直接用二维数组dp[maxn][maxm];
关键代码
for(i=1;i<=N;i++){
for(j=0;j<=W;j++){
for(k=0;k*w[i]<=j;k++)
dp[i][j]=max(dp[i][j],dp[i-1][j-k*w[i]]+k*v[i]);
}
}
优化策略:由01背包可将二位数组优化为一维数组可知,完全背包也能做类似优化,但由于比较的状态是当前状态,因此第二层遍历需顺序遍历
关键代码
for(i=1;i<=n;i++){
for(j=need[i];j<=m;j++){
if(j>=need[i])
dp[j]=max(dp[j],dp[j-need[i]]+value[i]);
}
}
多重背包
多重背包的基本模型就是有N种物品和一个容量为W的背包。第i种物品最多有num[i]件可用,每件占重是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。 把物品拆开,把相同的num[i]件物品 看成 价值跟重量相同的num[i]件不同的物品,那么就转化成了一个规模稍微大一点的01背包了 动态转移方程: dp[i][j] = max (dp[i-1][j],dp[i-1][j - k*weight[i]] +k*value[i] ) 还有其他效率更高的方法,能力有限,还不会-_-!!!
代码实现
关键代码
for(int i=1; i<=n; i++)//每种物品
for(int k=0; k<num[i]; k++)//其实就是把这类物品展开,调用num[i]次01背包代码
for(int j=m; j>=weight[i]; j--)//正常的01背包代码
dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);