01背包问题Java

本文介绍了01背包问题的动态规划解决方案。通过定义状态f[i][v]表示前i件物品放入容量为v的背包所能获得的最大价值,利用状态转移方程探讨物品放与不放的情况,最终实现寻找最大价值的算法。文中还提供了代码实现的基本思路和空间复杂度优化方向。

题目
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式
输出一个整数,表示最大价值。

数据范围
0<N,V≤1000
0<vi,wi≤1000
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
8

问题解决:

(1)将题中输入举例转换成表格,清晰表现,后续计算帮助理解:
题目例题信息

(2)总体思路:
动态规划
用子问题定义状态:即 f[i][v] 表示前 i 件物品恰放入一个容量为 v 的背包可以获得的最大价值。
则其状态转移方程便是:
状态转移方程

(3)01背包问题特点:每件物品仅有一件,选择放或不放
则该问题的子问题:前 i 件物品放入容量为 V 的背包中,考虑第 i 件物品放或不放
每件物品只有两种状态:放与不放,而在前i件物品中已经选择好放那些物品,然后考虑第 i 件放或不放是否会有当前最大价值即最优解。

  • 不放第i件物品:
    两种情况:1.背包剩余容积不足以装下第i件物品:则此时的价值与前i-1个物品价值一样: f[i][v] = f[i-1][v];
    2.有足够的容积,但是装了第i件不一定会达到当前最大价值,因此要在装与不装之间作比较,选较大者:f[i][v] = max ( f[i-1][v],f[i][v-c[i]]+w[i] );若结果是 f[i-1][v] 则不放第i件物品。

  • 放第i件物品:
    有足够的背包空间,并且选择放之后有当前最大价值,但同样直接作比较得出计算结果:f[i][v] = max ( f[i-1][v],f[i][v-c[i]]+w[i] );
    (4)根据状态转移方程可以将题中数据代入计算填表,进一步理解:

  • 1.初始化:f[0][0] = 0;
    初始化

  • 2.代入数据进行计算验证,如:
    i=1,j=1,w[1] = 2,v[1] = 1,j = v[1],f[1][1]= max ([f[1-1][1] , f[1-1][1-1]+2) ) = 2;
    一行一行的填表,得出结果,如下图 :
    计算验证
    得到最大价值为:f[N][V] = 8.

代码实现:

1.基本思路:

import java.util.Scanner;
public class Main {
   
   
    public static void main(String[] args) throws Exception {
   
   
        Scanner read1 = new Scanner(System.in);
        int N = read1.nextInt(); // 有N件物品;
        int V = read1.nextInt
### 0-1 背包问题Java 实现解决方案 #### 动态规划方法 动态规划是一种经典的解决 0-1 背包问题的方法。它通过构建一个二维数组 `dp` 来存储子问题的结果,其中 `dp[i][j]` 表示前 `i` 个物品在背包容量为 `j` 的情况下的最大价值。 以下是基于动态规划Java 实现: ```java public class Knapsack { public static int knapSack(int W, int[] wt, int[] val, int n) { int[][] dp = new int[n + 1][W + 1]; for (int i = 0; i <= n; i++) { for (int w = 0; w <= W; w++) { if (i == 0 || w == 0) { dp[i][w] = 0; } else if (wt[i - 1] <= w) { dp[i][w] = Math.max(val[i - 1] + dp[i - 1][w - wt[i - 1]], dp[i - 1][w]); } else { dp[i][w] = dp[i - 1][w]; } } } return dp[n][W]; } public static void main(String[] args) { int[] val = {60, 100, 120}; int[] wt = {10, 20, 30}; int W = 50; int n = val.length; System.out.println(knapSack(W, wt, val, n)); // 输出结果应为 220 } } ``` 上述代码实现了动态规划的核心逻辑[^4]。通过迭代计算每个状态的最大价值,最终得到全局最优解。 --- #### 递归方法 递归方法可以通过回溯的思想解决问题。对于每一个物品,可以选择将其放入背包或者不放入背包,从而形成两种可能的状态树。递归方法的时间复杂度较高,但由于其简单直观,在某些场景下仍然具有应用价值。 以下是基于递归的 Java 实现: ```java public class RecursiveKnapsack { public static int knapSack(int W, int[] wt, int[] val, int n) { if (n == 0 || W == 0) { return 0; } if (wt[n - 1] > W) { return knapSack(W, wt, val, n - 1); } else { return Math.max( val[n - 1] + knapSack(W - wt[n - 1], wt, val, n - 1), knapSack(W, wt, val, n - 1) ); } } public static void main(String[] args) { int[] val = {60, 100, 120}; int[] wt = {10, 20, 30}; int W = 50; int n = val.length; System.out.println(knapSack(W, wt, val, n)); // 输出结果应为 220 } } ``` 此代码展示了递归方法的基本思想[^1]。尽管这种方法易于理解,但在大规模数据集上可能会遇到性能瓶颈。 --- #### 分支限界法 分支限界法通过对搜索空间进行剪枝优化,减少不必要的计算量。该方法通常用于处理较大规模的数据集,并能有效降低时间复杂度。 以下是一个简单的分支限界法实现思路: ```java import java.util.*; public class BranchAndBoundKnapsack { private static class Node implements Comparable<Node> { int level, profit, bound, weight; @Override public int compareTo(Node other) { return Double.compare(other.bound, this.bound); // 按照边界值降序排列 } } public static int solveKnapsack(int capacity, int[] weights, int[] values, int itemCount) { PriorityQueue<Node> pq = new PriorityQueue<>(); double totalProfit = Arrays.stream(values).sum(); Node root = new Node(); root.level = -1; root.profit = 0; root.weight = 0; computeBound(root, capacity, weights, values, itemCount); pq.add(root); while (!pq.isEmpty()) { Node node = pq.poll(); if (node.bound > maxProfit && node.weight <= capacity) { maxProfit = node.profit; } if (node.level >= itemCount - 1) continue; Node nextInclude = new Node(); nextInclude.level = node.level + 1; nextInclude.weight = node.weight + weights[nextInclude.level]; nextInclude.profit = node.profit + values[nextInclude.level]; if (nextInclude.weight <= capacity && nextInclude.profit > maxProfit) { maxProfit = nextInclude.profit; } computeBound(nextInclude, capacity, weights, values, itemCount); if (nextInclude.bound > maxProfit) { pq.add(nextInclude); } Node nextExclude = new Node(); nextExclude.level = node.level + 1; nextExclude.weight = node.weight; nextExclude.profit = node.profit; computeBound(nextExclude, capacity, weights, values, itemCount); if (nextExclude.bound > maxProfit) { pq.add(nextExclude); } } return maxProfit; } private static void computeBound(Node u, int cap, int[] wt, int[] val, int n) { if (u.weight >= cap) { u.bound = 0; return; } u.bound = u.profit; int j = u.level + 1; int totweight = u.weight; while ((j < n) && (totweight + wt[j] <= cap)) { totweight += wt[j]; u.bound += val[j]; j++; } if (j < n) { u.bound += (cap - totweight) * (val[j] / (double) wt[j]); } } private static int maxProfit = Integer.MIN_VALUE; public static void main(String[] args) { int[] val = {60, 100, 120}; int[] wt = {10, 20, 30}; int W = 50; int n = val.length; System.out.println(solveKnapsack(W, wt, val, n)); // 输出结果应为 220 } } ``` 这段代码采用了优先队列来管理节点的选择顺序,并通过边界值计算加速收敛过程[^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值