0、1背包问题 完全背包

0/1背包

给定 N 件物品,第i个物品的重量为 weight[i],价值为value[i]。现挑选物品放入背包中,假定背包能承受的最大重量为 V,问应该如何选择装入背包中的物品,使得装入背包中物品的总价值最大?

  • “已经处理的物品数” 作为DP的阶段,
  • 以“背包中已经放入的物品总体积”作为附加维度

F[i, j] 表示从前i个物品中选出了总体积为j的物品放入背包
F [ i , j ] = max ⁡ { F [ i − 1 , j ]           不 选 用 第 i 个 物 品 F [ i − 1 , j − v a l u e [ i ] ] + w e i g h t [ i ]     i f   j > = w e i g h t [ i ]   选 用 第 i 个 物 品 F[i,j]=\max\left\{ \begin{array}{l} F[i-1,j] ~~~~~~~~~不选用第i个物品\\ F[i-1,j-value[i]]+weight[i]~~~ if~j>= weight[i] ~选用第i个物品\\ \end{array} \right. F[i,j]=max{F[i1,j]         iF[i1,jvalue[i]]+weight[i]   if j>=weight[i] i初值:F[0,0] = 0,其余均为负无穷,目标max{F[N, j]} 0<=j<=V

https://www.cnblogs.com/kkbill/p/12081172.html
https://labuladong.github.io/ebook/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%B3%BB%E5%88%97/%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98.html

//  时间O(N*V) 空间O(N*V)
    public int maxValue1(int[] weight, int[] value, int V) {
        int N = weight.length;
        if (N == 0) return 0;

        int[][] dp = new int[N+1][V+1];

//dp[i][w] 的定义如下:
// 对于前 i 个物品,当前背包的容量为 w,这种情况下可以装的最大价值是 dp[i][w]
        for (int i = 1; i <= N; i++) {
            for (int j = 1; j <= V; j++) {
                if (j - weight[i-1] < 0) {
                    // 这种情况下只能选择不装入背包
                    dp[i][j] = dp[i-1][j];
                } else {
                    // 装入或不装入,择优
                    dp[i][j] = Math.max(dp[i-1][j-weight[i-1]] + value[i-1],
                                        dp[i-1][j]);
                   // 使用的是i-1阶段的j-weight[i-1],j                
                }
            }
        }

        return dp[N][V];
    }

    //  时间O(N*V) 空间O(V)
    public int maxValue2(int[] weight, int[] value, int V) {
        int N = weight.length;

        int[] dp = new int[V+1];

        for (int i = 1; i <= N; i++) {
            for (int j = V; j >= 1; j--) {   //
                if (j - weight[i-1] < 0) {
                    dp[j] = dp[j];
                } else {
                    dp[j] = Math.max(dp[j],
                                     dp[j-weight[i-1]] + value[i-1]);
                }
            }
        }
        return dp[V];
    }

    //  时间O(N*V) 空间O(V)
    public int maxValue3(int[] weight, int[] value, int V) {
        int N = weight.length;

        int[] dp = new int[V+1];

        for (int i = 1; i <= N; i++) {
            for (int j = V; j >= weight[i-1]; j--) {  // 倒序
                dp[j] = Math.max(dp[j], dp[j-weight[i-1]] + value[i-1]);
                // 如果正序的话,使用的j-weight[i-1],就是i阶段的而不是i-1阶段的了
            }
        }
        return dp[V];
    }

完全背包

给定 N 件物品,第i个物品的重量为 weight[i],价值为value[i],并且有无数个。现挑选物品放入背包中,假定背包能承受的最大重量为 V,问应该如何选择装入背包中的物品,使得装入背包中物品的总价值最大?

F[i,j]表示从前i种物品中选出了总质量为j的物品放入背包,物品的最大价值和。
F [ i , j ] = max ⁡ { F [ i − 1 , j ]           尚 未 选 过 第 i 种 物 品 F [ i − 1 , j − v a l u e [ i ] ] + w e i g h t [ i ]     i f   j > = w e i g h t [ i ]   从 第 i 种 物 品 中 选 一 个 F[i,j]=\max\left\{ \begin{array}{l} F[i-1,j] ~~~~~~~~~尚未选过第i种物品\\ F[i-1,j-value[i]]+weight[i]~~~ if~j>= weight[i] ~从第i种物品中选一个\\ \end{array} \right. F[i,j]=max{F[i1,j]         iF[i1,jvalue[i]]+weight[i]   if j>=weight[i] i初值:F[0,0] = 0,其余均为负无穷,目标max{F[N, j]} 0<=j<=V

 for i ← 1 to N 
 		for v ← Ci to V 
 				F[i, v] ← max{F[i − 1, v], F[i − 1, v − Ci] + Wi}


def CompletePack(F, C, W) 
	for v ← C to V
		 F[v] ← max{F[v], f[v − C] + W}

多重背包

给定 N 件物品,第i个物品的重量为 weight[i],价值为value[i],并且有nums[i]个。现挑选物品放入背包中,假定背包能承受的最大重量为 V,问应该如何选择装入背包中的物品,使得装入背包中物品的总价值最大?

因为对于第 i 种物品有 nums[i]+ 1 种策略:取 0 件,取 1 件……取 nums[i]件。
令 F[i, v] 表示前 i 种物品恰放入一个容量为 v 的背包的最大价值,
则有状态转移方程:
F [ i , v ] = max ⁡ { F [ i − 1 , v − k ∗ w e i g h t [ i ] ] + k ∗ v a l u e [ i ]        ∣ 0 ≤ k ≤ M i } . F[i,v]=\max\left\{ F[i − 1, v − k ∗ weight[i]] + k ∗ value[i]~~~~~~| 0 ≤ k ≤ Mi\right\}. F[i,v]=max{F[i1,vkweight[i]]+kvalue[i]      0kMi}.复杂度是 O(V Σnums[i])。

分组背包

给定 N 件物品,第i组有个nums[i]个物品。第i组的第j个物品的重量为 weight[i][j],价值为value[i][j]。现挑选物品放入背包中,假定背包能承受的最大重量为 V,要求选择若干个物品放入背包,使得每组之多选择一个物品并且物品总质量不超过V的前提下,使得装入背包中物品的总价值最大?

F [ i , j ] = max ⁡ { F [ i − 1 , j ]           不 选 第 i 组 物 品 max ⁡ 1 < = k < = n u m s [ i ] F [ i − 1 , j − w e i g h t [ i ] [ k ] ] + v a l u e [ i ] [ k ]     i f   j > = w e i g h t [ i ]   选 第 组 的 某 个 物 品 k F[i,j]=\max\left\{ \begin{array}{l} F[i-1,j] ~~~~~~~~~不选第i组物品\\ \max_{1<=k<=nums[i]}F[i-1,j-weight[i][k]]+value[i][k]~~~ if~j>= weight[i] ~选第组的某个物品k\\ \end{array} \right. F[i,j]=max{F[i1,j]         imax1<=k<=nums[i]F[i1,jweight[i][k]]+value[i][k]   if j>=weight[i] k

和前几个背包模型一样,我们可以省略掉F数组的第一维,用j的倒叙循环来空控制“阶段i”的状态只能从“阶段i-1”转移而来。

for (int i = 1; i <= N; i++) 
	for (int j = V; j >= 0; j--)
		for (int k = 1; k <= nums[i]; k++)
			if (j >= weight[i][k])
				dp[j] = Math.max(dp[j], dp[j-weight[i][k]] + value[i][k];
  • 除了倒序循环j之外,对于每一组内nums[i]个物品的循环k应该放在j的内层。
  • 从背包的角度看,这是因为每组内之多选择一个物品,若把k置于j的外层,就会类似于多重背包,每组物品在F数组上的转移会产生积累,最终可以选择超过1个物品。
  • 从动态规划的角度,i是“阶段”,i和j共同构成“状态”,而k是“决策”----在第i组内使用哪一个物品,这三者的顺序绝对不能混淆。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值