动态规划:01 背包(闫氏DP分析法)

动态规划:01 背包(闫氏DP分析法)

01 背包

www.acwing.com/problem/content/2/

DP:

  • 状态表示 f(i, j)

    • 集合:所有只考虑前 i i i 个物品,且总体积不超过 j j j 的选项的集合

    • 属性:max

      • 最终答案:f(N, V)
  • 状态计算:f(i, j) = max(f(i - 1, j), f(i - 1, j - v[i]) + w[i])

    • 选第 i i i 个物品:所有包含第 i i i 个物品的选项集合,其实需要找的就是变化的部分的最大值,即:从 1 1 1~ i − 1 i-1 i−1 中选择,且总体积小于等于 j − v [ i ] j-v[i] j−v[i] 的选项集合

      当 j < v [ i ] j < v[i] j<v[i] 时,该分支不存在

      • 变化的部分:包含前 i − 1 i-1 i−1 个物品的不同选项
      • 不变的部分:都需要包含第 i i i 个物品
    • 不选第 i i i 个物品:需要满足从 1 1 1~ i − 1 i-1 i−1 中选择,且总体积小于等于 j j j 的所有选项集合

二维朴素写法

java 复制代码
import java.util.*;

public class Main {
    static final int N = 1010;
    // f[i][j] 表示只对于前i个物品且使用j的背包空间,选取的所有方案中的最大值
    static int[][] f = new int[N][N];
    static int[] w = new int[N];
    static int[] v = new int[N];
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int V = sc.nextInt();
        
        for (int i = 1; i <= n; i++) {
            v[i] = sc.nextInt();
            w[i] = sc.nextInt();
        }
        
        // dp
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= V; j++) {
                if (j >= v[i]) {
                    // j >= v[i] 能够将第i个物品放入
                    // 放入第i个物品
                    f[i][j] = f[i - 1][j - v[i]] + w[i];
                }
                // 不放入第i个物品
                f[i][j] = Math.max(f[i][j], f[i - 1][j]);
            }
        }
        
        System.out.println(f[n][V]);
    }
}

一维优化写法

java 复制代码
import java.util.*;

public class Main {
    static final int N = 1010;
    // 一维优化,相当于每次的i直接覆盖在上一次的i-1数组上
    // 因为每一次只会用到上一层的,不会用到更上面的数据
    // 且每次j-v[i]只会用在j之前的,当使用从大到小遍历时,当前修改不会影响到后面的答案
    // 所以可以使用滚动数组优化
    static int[] f = new int[N];
    static int[] w = new int[N];
    static int[] v = new int[N];
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int V = sc.nextInt();
        
        for (int i = 1; i <= n; i++) {
            v[i] = sc.nextInt();
            w[i] = sc.nextInt();
        }
        
        // dp
        for (int i = 1; i <= n; i++) {
            // 想清楚为什么要从大到小
            // 因为遍历到j时要使用上一层的j-v[i],所以应当先遍历大的才能避免影响
            for (int j = V; j >= 0; j--) {
                if (j >= v[i]) {
                    // j >= v[i] 能够将第i个物品放入
                    // 不放入第i个物品, 放入第i个物品
                    f[j] = Math.max(f[j], f[j - v[i]] + w[i]);    
                }
            }
        }
        
        System.out.println(f[V]);
    }
}
相关推荐
java1234_小锋2 分钟前
Redis到底支不支持事务啊?
java·数据库·redis
爱笑的眼睛1127 分钟前
超越`cross_val_score`:深入剖析Scikit-learn交叉验证API的设计哲学与高阶实践
java·人工智能·python·ai
L0CK33 分钟前
三种依赖注入详解
java
业精于勤的牙39 分钟前
三角形最小路径和(二)
算法
风筝在晴天搁浅40 分钟前
hot100 239.滑动窗口最大值
数据结构·算法·leetcode
夏乌_Wx1 小时前
练题100天——DAY31:相对名次+数组拆分+重塑矩阵
数据结构·算法
LYFlied1 小时前
【算法解题模板】-解二叉树相关算法题的技巧
前端·数据结构·算法·leetcode
shoubepatien1 小时前
JAVA -- 07
java·后端·intellij-idea
Gu_yyqx1 小时前
Maven进阶
java·maven
小肖爱笑不爱笑1 小时前
JavaScript
java·javascript·json·web