java 完全背包问题

java 完全背包问题

问题描述

在完全背包问题中,给定一个背包的最大容量,和一系列物品,每种物品都有无限个可用,每个物品有自己的重量和价值。目标是在不超过背包容量的情况下,使得背包中物品的总价值最大。

与01背包的比较

在01背包问题中,每种物品只有一个可用,而在完全背包问题中,每种物品都有无限个可用。这意味着我们可以选择多次同一种物品放入背包中。

示例物品

假设背包的最大重量为6,且有以下物品:

  • 物品1:重量1,价值3
  • 物品2:重量3,价值2
  • 物品3:重量6,价值5

二维dp数组解决

解决思路

我们可以使用动态规划来解决完全背包问题。我们定义一个二维的动态规划数组 dp,其中 dp[i][j] 表示在前 i 个物品中,背包容量为 j 时的最大总价值。

状态转移方程

在状态转移方程中,我们需要考虑当前物品放入背包中的多种情况:

  • 如果不放入当前物品,则 dp[i][j] = dp[i-1][j]
  • 如果放入当前物品,因为物品可以重复放入,我们可以选择多次放入当前物品直到超出背包容量,所以 dp[i][j] = max(dp[i-1][j], dp[i][j-weights[i]] + values[i])

初始化

我们需要对动态规划数组进行初始化,当没有物品时或背包容量为0时。

演算流程

物品\容量 0 1 2 3 4 5 6 7 8
无物品 0 0 0 0 0 0 0 0 0
物品1 0 2 4 6 8 10 12 14 16
物品1,2 0 2 4 6 8 10 12 14 16
物品1,2,3 0 2 4 6 8 10 12 14 16

Java代码

java 复制代码
public class Bag01 {
    public int knapsack(int[] weight, int[] value, int capacity) {
        int n = weight.length;
        int[][] dp = new int[n + 1][capacity + 1];
        
        // 遍历物品
        for (int i = 1; i <= n; i++) {
            // 遍历背包承重
            for (int j = 1; j <= capacity; j++) {
                // 如果当前物品重量大于背包承重,无法放入背包
                if (weight[i - 1] > j) {
                    dp[i][j] = dp[i - 1][j];
                } else {
                    // 取放入和不放入背包的最大值
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - weight[i - 1]] + value[i - 1]);
                    /*!!与01背包不同的地方在这里,放入物品时,不是使用dp[i - 1][j - weight[i - 1]],
                    而是使用dp[i][j - weight[i - 1]],表示可以重复选择当前物品*/
                }
            }
        }
        for (int i = 0; i < dp.length; i++) {
            for (int j = 0; j < dp[i].length; j++) {
                System.out.print(dp[i][j] + " ");
            }
            System.out.println();
        }

        // 返回结果
        return dp[n][capacity];
    }

    public static void main(String[] args) {
        Bag01 knapsack = new Bag01();
        int[] weight = {1, 3, 6};
        int[] value = {2, 5, 8};
        int capacity = 8;
        System.out.println("最大价值为: " +knapsack.knapsack(weight, value, capacity)); // 输出:16

    }
}

与01背包的不同

在完全背包问题中,与01背包问题不同之处在于,我们可以重复选择当前物品放入背包中,因此在更新状态时,如果选择放入当前物品,应该使用dp[i][j - weight[i - 1]]而不是dp[i - 1][j - weight[i - 1]]

一维滚动数组解决(推荐)

Java代码

java 复制代码
public class Main {
    // 定义一个方法,用于解决完全背包问题
    public static int knapsack(int[] weights, int[] values, int capacity) {
        // 获取物品数量
        int n = weights.length;
        // 创建一维数组,用于保存背包容量对应的最大价值
        int[] dp = new int[capacity + 1];// 数组大小定义记得是容量+1

        // 遍历每个物品
        for (int i = 0; i < n; i++) {
            // 逆序遍历背包容量,确保可以多次选择同一物品(与01背包不同)
            for (int j = weights[i]; j <= capacity; j++) {// 正序遍历,与01背包不同
                // 使用状态转移方程更新当前背包容量的最大价值
                dp[j] = Math.max(dp[j], dp[j - weights[i]] + values[i]);
            }
        }

        // 返回背包容量为capacity时的最大价值
        return dp[capacity];
    }

    public static void main(String[] args) {
        // 给定物品的重量和价值,以及背包的容量
        int[] weights = {1, 3, 6};
        int[] values = {2, 5, 8};
        int capacity = 8;

        // 调用knapsack方法计算最大价值
        int max_value = knapsack(weights, values, capacity);
        // 输出结果
        System.out.println("可以获得的最大价值:" + max_value);
    }
}

与01背包不同之处:

  1. 在遍历背包容量时,使用正序遍历(jweights[i]capacity),以确保可以多次选择同一物品放入背包中。
  2. 在状态转移方程中,不再需要判断背包容量是否大于等于物品重量,因为完全背包中可以多次选择同一物品。
相关推荐
AlunYegeer1 小时前
JAVA,以后端的视角理解前端。在全栈的路上迈出第一步。
java·开发语言·前端
浅念-1 小时前
刷穿LeetCode:BFS 解决 Flood Fill 算法
数据结构·c++·算法·leetcode·职场和发展·bfs·宽度优先
hixiong1231 小时前
C# OpenvinoSharp使用DINOv2模型进行图像相似度计算
开发语言·c#
做cv的小昊2 小时前
【TJU】研究生应用统计学课程笔记(8)——第四章 线性模型(4.1 一元线性回归分析)
笔记·线性代数·算法·数学建模·回归·线性回归·概率论
DFT计算杂谈2 小时前
自动化脚本一键绘制三元化合物相图
java·运维·服务器·开发语言·前端·python·自动化
2301_771717212 小时前
Spring Boot 自动配置核心注解
java·spring boot·mybatis
小Y._2 小时前
面试被问synchronized锁升级,这5个问题答不上来直接挂!
java
EW Frontier2 小时前
6G ISAC新范式:基于智能漏波天线的Wi‑Fi通感一体化系统设计与实测【附MATLAB+python代码】
开发语言·python·matlab·music·isac·doa·wi‑fi
姚青&2 小时前
测试技术体系
java·python
贾斯汀玛尔斯2 小时前
每天学一个算法--倒排索引(Inverted Index)
算法·inverted-index