完全背包问题

完全背包问题

完全背包是背包问题的基础问题之一,和前面介绍的01背包类似,唯一的不同的是每件物品不再是只有一件,而是无限件

01背包前面已经做了简单的总结。https://blog.csdn.net/u014532901/article/details/79835712

完全背包问题的描述如下:

给定nn件物品,对于第ii件物品,其价值为vivi,重量为wiwi,与此同时还存在一个体积为VV的背包,每件物品有无限件,求背包能装下的物品的最大价值PP

对应的模型为:

s.t.  ni=1 wixi<=V,xiNs.t.  ∑i=1n wi∗xi<=V,xi∈N

maxP=ni=1viximaxP=∑i=1nvi∗xi

解法

解法同01背包十分类似,状态转移方程为:

{fi,j=fi1,jif wi>jfi,j=max(fi1,j,fi,jwi+vi) if wi<=j{fi,j=fi−1,jif wi>jfi,j=max(fi−1,j,fi,j−wi+vi) if wi<=j
  • 01背包的状态转移方程是:fi,j=max(fi1,j,fi1,jwi+vi)fi,j=max(fi−1,j,fi−1,j−wi+vi)
  • 完全背包的状态转移方程是:fi,j=max(fi1,j,fi,jwi+vi)fi,j=max(fi−1,j,fi,j−wi+vi)

fi,jfi,j表示前ii种物品装进容量为jj的背包里面获取的最大价值,因此对于第ii件物品来放进大小为jj的背包来讲:

  • 如果wi>jwi>j,那么该物品放不进去,则此时的收益和前i1i−1件物品放进大小为jj的背包的最大收益一样,即fi,j=fi1,jfi,j=fi−1,j
  • 若果wi<=jwi<=j, 则该物品可以放进去,但是此时是有两种选择的,即放进去或者不放进去,因此需要评估两种选择的收益大小:
    • 将第ii件物品不放进去:收益为fi1,jfi−1,j
    • 将第ii件物品放进去,但是由于第ii件物品有无限件,同01背包中不一样,我们的上一个状态不仅仅可以是:i1i−1件商品放进背包大小为jwij−wi中的最大收益值,还可以更进一步选择更新的状态:ii件物品放进大小为jwij−wi的背包中的最大收益值。此时前一个状态是:i1i−1件物品放进大小为jwij−wi的背包中。因此将第ii件物品放入背包的收益是fi,jwi+vifi,j−wi+vi
    • 最后两个方案中最大的收益便是当前的收益

代码和01背包代码类似:


    public static int knapsackProblemCompletion(int[] value, int[] weight, int bagV) {
        int n = value.length;
        int[][] dp = new int[value.length][bagV + 1];
        for (int i = 1; i < n; i++) {
            for (int j = 1; j <= bagV; j++) {//注意这里j是从1开始的
                if (weight[i] > j) {
                    dp[i][j] = dp[i-1][j];
                }
                else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - weight[i]] + value[i]);
                }
            }
        }

        return dp[n - 1][bagV];
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int[] value = new int[] { 0, 1, 4, 3,6};// 物品的价值
        int[] weight = new int[] { 0, 5, 2, 4, 3};// 物品的重量
        int bagV = 15;// 背包的大小
        System.out.println(knapsackProblemCompletion(value, weight, bagV));
    }

算法的时间复杂度为: O(NV)O(NV),空间复杂度为:O(NV)O(NV)

二维状态数组可以用如下的二维表格表示:

这里写图片描述

空间优化

由前面的状态转移方程我们可以知道,fi,jfi,j的状态只和前一轮的状态fi1,jfi−1,j和本轮的稍早的某个状态fi,jwifi,j−wi有关,因此可以像01背包问题一样,将二维数组优化为一维滚动数组,优化后空间复杂度降为O(V)O(V)


public static int knapsackProblemCompletionOptimization(int[] value, int[] weight, int bagV) {
        int n = value.length;
        int[] dp = new int[bagV+1];
        for (int i = 1; i < n; i++) {
            for (int j = weight[i]; j <= bagV; j++) {
                dp[j] = Math.max(dp[j], dp[j-weight[i]] + value[i]);
            }
        }
        return dp[bagV];
    }

特别注意这里的内部循环的循环顺序:

for (int j = weight[i]; j <= bagV; j++) 

状态回溯

有时候我们不仅仅需要找到能到到最优收益值,还需要找到具体的装包方案,我们可以根据状态数组从后往前倒推得出装包方案。


    public static int[] tracbackCompletion(int[][] dp, int[] weight, int bagV) {
        int[] res = new int[weight.length];
        int j = bagV;
        for (int i = weight.length - 1; i >= 1; i--) {
            //从后往前追踪,dp[i][j]要么来自dp[i-1][j],要么来自dp[i][j-weight[i]]
            while (dp[i][j] != -1 && dp[i][j] != dp[i-1][j]) {
                res[i] += 1;
                j -= weight[i];
            }
        }
        res[0] = dp[0][j] > 0 ? res[0] + 1 : res[0];
        return res;
    }

这里写图片描述

总结

完全背包是背包问题的基础问题问题之一,和01背包非常类似但是也有些许区别。
完全背包和01背包的区别:完全背包的物品是无限的,而01背包的物品只有一件。

两者的状态转移方程为:

  • 01背包的状态转移方程是:fi,j=max(fi1,j,fi1,jwi+vi)fi,j=max(fi−1,j,fi−1,j−wi+vi)
  • 完全背包的状态转移方程是:fi,j=max(fi1,j,fi,jwi+vi)fi,j=max(fi−1,j,fi,j−wi+vi)
原文地址:https://www.cnblogs.com/Spground/p/9567880.html