背包九讲–多重背包问题
前言:最近在看背包九讲,记一下笔记顺便将其中的代码实现一下
这个多重背包的问题较好理解,完全背包问题是物品不限次的拿,而多重背包问题是物品有限次的拿,因此当物品的重量乘以物品的个数大于总背包重量时,相当于完全背包问题,否则就通过01背包问题来解决
代码实现:
01背包问题实现
public class PackageProblem01 {
public void PackageProblem02Solution(int[] dp, int C, int W, int weight)
{
int i;
for(i=weight; i>=C; i--)
dp[i] = Math.max(dp[i], dp[i-C]+W);
}
}
完全背包问题实现
public class PackageProblem02 {
public void PackageProblem02Solution(int[] dp, int C, int W, int weight)
{
int i;
for(i=C; i<=weight; i++)
dp[i] = Math.max(dp[i], dp[i-C]+W);
}
}
多重背包问题实现
import java.util.Arrays;
public class MultiplePack {
private PackageProblem01 p01 = new PackageProblem01();
private PackageProblem02 p02 = new PackageProblem02();
public void MulitplePcakSolution(int[] dp, int M, int W, int C, int V)
{
if(M*C>=V)
{
p02.PackageProblem02Solution(dp, C, W, V);
return;
}
int k=1;
while(k<M)
{
p01.PackageProblem02Solution(dp, k*C,k*W, V);
M = M - k;
k = 2*k;
}
p01.PackageProblem02Solution(dp, M*C, M*W, V);
}
public static void main(String[] args) {
int V = 15;
int[] M = {1,3,5,7,9};
int[] C = {1,3,5,7,9};
int[] W = {1,3,5,7,9};
int[] dp = new int[V+1];
int i;
MultiplePack m = new MultiplePack();
for(i=0; i<M.length; i++)
{
m.MulitplePcakSolution(dp, M[i], W[i], C[i], V);
System.out.println(Arrays.toString(dp));
}
}
}
可以观察一下输出结果
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[0, 1, 1, 3, 4, 4, 6, 7, 7, 9, 10, 10, 10, 10, 10, 10]
[0, 1, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
[0, 1, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
[0, 1, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
可行性问题 O(VN)的算法
这个伪代码一开始看很晕,感觉不知所云
F[0, 1 . . . V ] ← −1
F[0, 0] ← 0
for i ← 1 to N
for j ← 0 to V
if F[i − 1][j] ≥ 0
F[i][j] = M[i]
else
F[i][j] = −1
for j ← 0 to V − Ci
if F[i][j] > 0
F[i][j + Ci] ← max{F[i][j + Ci], F[i][j] − 1}
但是我们根据F[i][j]
的含义来理解代码就会更清楚一点,F[i][j]表示的是使用前i种物品填满容量为j的背包时第i种物品所剩下的最大个数
首先,初始化
F[0, 1 . . . V ] ← −1
F[0, 0] ← 0
这段代码表示,使用0种物品没办法填装满容量大于1的背包,但是可以填满容量为0的背包
第一层循环
for i ← 1 to N
表示由前几种物品组合,从1开始累加
第二层循环其一
for j ← 0 to V
if F[i − 1][j] ≥ 0
F[i][j] = M[i]
else
F[i][j] = −1
这段代码表示从0开始增加容量时,如果前i-1种物品没有填满,有两种情况,第一种是背包不合适,也就是一个物品都装不下,那这个时候肯定就不考虑了,直接将F[i][j]=-1
,另外一种情况就是前面所有物品都放下去了还填不满容量为j的背包,这种情况下面的代码会进行补偿
第二层循环其二
for j ← 0 to V − Ci
if F[i][j] > 0
F[i][j + Ci] ← max{F[i][j + Ci], F[i][j] − 1}
这段代码就表示将一个当前物品假如背包后,所剩物品的个数应该为F[i][j+ci]和当前物品数减1的更大者
Java实现算法
import java.util.Arrays;
public class MultiplePack {
public static void main(String[] args) {
int V = 10;
int[] M = {0,3,4,6,9};
int[] C = {0,3,4,6,9};
int[][] dp = new int[M.length][V+1];
int i, j;
for(i=0; i<dp.length; i++)
Arrays.fill(dp[i], -1);
dp[0][0] = 0;
for(i=1; i<M.length; i++)
{
for(j=0; j<=V; j++)
{
if(dp[i-1][j]>=0)
dp[i][j]=M[i];
else
dp[i][j] = -1;
}
for(j=0; j<=V-C[i]; j++)
{
if(dp[i][j]>0)
dp[i][j+C[i]] = Math.max(dp[i][j+C[i]], dp[i][j]-1);
}
for(j=0; j<dp.length; j++)
System.out.println(Arrays.toString(dp[j]));
System.out.println("--------------------");
}
}
}
我们看一下输出结果,观察一下dp数组的变化
[0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
[3, -1, -1, 2, -1, -1, 1, -1, -1, 0, -1]
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
--------------------
[0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
[3, -1, -1, 2, -1, -1, 1, -1, -1, 0, -1]
[4, -1, -1, 4, 3, -1, 4, 3, 2, 4, 3]
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
--------------------
[0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
[3, -1, -1, 2, -1, -1, 1, -1, -1, 0, -1]
[4, -1, -1, 4, 3, -1, 4, 3, 2, 4, 3]
[6, -1, -1, 6, 6, -1, 6, 6, 6, 6, 6]
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
--------------------
[0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
[3, -1, -1, 2, -1, -1, 1, -1, -1, 0, -1]
[4, -1, -1, 4, 3, -1, 4, 3, 2, 4, 3]
[6, -1, -1, 6, 6, -1, 6, 6, 6, 6, 6]
[9, -1, -1, 9, 9, -1, 9, 9, 9, 9, 9]
--------------------
我觉得我理解的还不是很深,但是看着这个变化大概能理解,你们也可以观察一下这个dp数组
的变化
参考:
《背包九讲》