背包问题__完全背包

完全背包

完全背包是指,在N个物品中,每个物品都有无限个。其中每个物品的体积为 vi[i], 每个物品的权重(价值)为 wi[i]。

对于每件物品,跟01背包相同,只有选和不选两种情况。区别在于,该物品可以选择一个或多个。

在一个体积为V的背包中,怎么选择放入物品,可使得价值最大。

注:本帖子中的所有内容,均以java代码进行演示

问题一

在这里插入图片描述

点击题目,跳转到原题。

剖析;

​ 完全背包,跟 01背包,大抵相同。所以,直接分析和 01背包 的不同。

​ 在01背包中,一个物品,只有选和不选,两种情况。而对于完全背包,则在此基础上,多了对于一个物品,若选择该物品,则应该选择多少件。注意到本题中的,数据范围很小,则可以考虑,直接枚举该物品选择多少件

dp[i] [j] 表示,只是用前 i 件物品(包括第 i 件物品),在体积为 j 的情况下,能够获得的最大价值。

​ 情况一:若不选择第 i 件物品,则获得的价值为 dp【i-1】【j】

​ 情况二:若选择第 i 件物品,并且该物品选择 k 件,所获得的价值为 dp【i-1】【j -v [i] * k】+ k * w[i]k 可取0-正无穷,如果条件满足的情况下。因为,在完全背包中,每件物品的个数为正无穷。当 k 为0时,即为情况一

抽取输入输出,打印以及main函数如下:

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        //scanner.next() 标准输出函数
        //物品数量
        int n = scanner.nextInt();
        //最大体积
        int v = scanner.nextInt();
        //第i件物品所占的体积
        int[] vi = new int[n + 1];
        //第i件物品的权重
        int[] wi = new int[n + 1];
        // 接下来有 N 行,每行有两个整数:v[i],w[i],用空格隔开,分别表示第i件物品的体积和价值
        for (int i = 1; i <= n; i++) {
            vi[i] = scanner.nextInt();
            wi[i] = scanner.nextInt();
        }
        allBackpacker(n, v, vi, wi);
    }
}

完全背包,直接枚举 第 i 件物品,所选择的个数,代码实现如下

//二维数组,三重循环
private static void allBackpacker(int n, int v, int[] vi, int[] wi) {
    int[][] dp = new int[n + 1][v + 1];
    //只是用前i件物品,包括第i件
    for (int i = 1; i <= n; i++) {
        //体积为j
        for (int j = 0; j <= v; j++) {
            //选择第 i 件物品的数量为 k
            for (int k = 0; k * vi[i] <= j; k++)
                dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - k * vi[i]] + k * wi[i]);
        }
    }
    System.out.println(dp[n][v]);
}

观察 dp[i] [j] 可得
在这里插入图片描述
故,上述代码可以优化为:

//二维数组,两重循环
private static void allBackpacker(int n, int v, int[] vi, int[] wi) {
    int[][] dp = new int[n + 1][v + 1];
    //只是用前i件物品,包括第i件
    for (int i = 1; i <= n; i++) {
        //体积为j
        for (int j = 0; j <= v; j++) {
            //不使用这个物品
            dp[i][j] = dp[i - 1][j];
            //使用这个物品
            if (j >= vi[i])
                //由于 j 是递增的,所以j - vi[i] 会在j 之前被计算
                dp[i][j] = Math.max(dp[i][j], dp[i][j - vi[i]] + wi[i]);
        }
    }
    System.out.println(dp[n][v]);
}

根据 01背包 ,可知,能够利用滚动数组,把二维数组优化为一维的。

由于在计算 dp[i] [j] ,的过程中,只用到了 第 i 个物品,并没有用到第 i-1 个物品,故可以将 dp 直接优化为一维数组。

优化如下:

//一维数组
private static void allBackpacker(int n, int v, int[] vi, int[] wi) {
    int[] dp = new int[v + 1];
    for (int i = 1; i <= n; i++) {
        //j小于vi[i]的部分,会在其他物品循环的时候,被计算到
        for (int j = vi[i]; j <= v; j++) {
            //dp方程都跟第i层有关,所以,可以直接转化为一维
          //dp[i][j] = Math.max(dp[i][j], dp[i][j - vi[i]] + wi[i]);
            dp[j] = Math.max(dp[j], dp[j - vi[i]] + wi[i]);
        }
    }
    System.out.println(dp[v]);
}

这里,我们可以推出一个一般性的结论,如果dp在计算过程中, 体积 j 用到了上一层(即 i-1 层)中比 j 小的dp 值,那么 j 应该 从大到小,进行遍历。因为,如果 j 从小到大,进行遍历的话,在一维数组中,会将上一层dp 的值覆盖掉。否则,则应该从小到大进行遍历。
感谢您的阅读!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值