当前位置: 首页 > article >正文

代码随想录算法训练营第四十一天 | 01背包问题(二维),01背包问题(一维),416.分割等和子集

01背包问题(二维)及背包问题理论基础

背包问题理论讲解及题目讲解:代码随想录
重点:
理解01背包二维数组的dp数组的含义,递推公式,初始化及遍历顺序
思路:

  1. dp数组的含义
// 从物品[0-i]里任意取几个,放进容量为j的背包,最大是多少
int[][] dp = new int[n][bagweight + 1];
  1. 递推公式
// 当前背包容量放不进物品i
if (j < weight[i]) {
   dp[i][j] = dp[i - 1][j];
// 当前背包能放进物品i,则递推公式有两种情况,取不放物品i和放物品i的最大值
} else {
   dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
  1. dp数组初始化
// 背包容量为0时,价值最大为0
for (int i = 0; i < n; i++) {
   dp[i][0] = 0;
}
// 只能物品0可以选择是否放入
for (int j = 0; j <= bagweight; j++) {
   // 背包容量比物品0小
   if (j < weight[0]) {
       dp[0][j] = 0;
   // 背包容量比物品0大
   } else {
       dp[0][j] = value[0];
   }
}
  1. 遍历顺序
// 先遍历物品再遍历背包
for (int i = 1; i < n; i++)
	for (int j = 0; j <= bagweight; j++)
  1. 模拟dp数组
public static void main(String[] args) {
    // 读取输入
    Scanner scanner = new Scanner(System.in);
    int n = scanner.nextInt();
    int bagweight = scanner.nextInt();
    int[] weight = new int[n];
    int[] value = new int[n];
    for (int i = 0; i < n; i++) {
        weight[i] = scanner.nextInt();
    }
    for (int i = 0; i < n; i++) {
        value[i] = scanner.nextInt();
    }
    // 从物品[0-i]里任意取几个,放进容量为j的背包,最大是多少
    int[][] dp = new int[n][bagweight + 1];
    // 背包容量为0时,价值最大为0
    for (int i = 0; i < n; i++) {
        dp[i][0] = 0;
    }
    // 只能物品0可以选择是否放入
    for (int j = 0; j <= bagweight; j++) {
        // 背包容量比物品0小
        if (j < weight[0]) {
            dp[0][j] = 0;
        // 背包容量比物品0大
        } else {
            dp[0][j] = value[0];
        }
    }
    // 先遍历物品再遍历背包
    for (int i = 1; i < n; i++) {
        for (int j = 0; j <= bagweight; j++) {
            // 当前背包容量放不进物品i
            if (j < weight[i]) {
                dp[i][j] = dp[i - 1][j];
            // 当前背包能放进物品i,则递推公式有两种情况,取不放物品i和放物品i的最大值
            } else {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
            }
        }
    }

    System.out.println(dp[n - 1][bagweight]);
}

01背包问题(一维)及背包问题理论基础

题目讲解:代码随想录
重点:
理解01背包一维数组的递推公式,遍历顺序
思路:

  1. dp数组的含义
// 容量为j的背包,所能背的最大价值
int[] dp = new int[bagweight + 1];
  1. 递推公式
// dp[j] 相当于 二维dp数组中的dp[i-1][j],即不放物品i,
// dp[j - weight[i]] + value[i],即放物品i,取最大的,毕竟是求最大价值
if (j < weight[i]) {
   dp[j] = dp[j];
} else {
   dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
}
  1. dp数组初始化
// 这样才能让dp数组在递归公式的过程中取的最大的价值,而不是被初始值覆盖了
for (int i = 0; i < dp.length; i++) {
   dp[i] = 0;
}
  1. 遍历顺序
// 先正序遍历物品,再倒序遍历背包
// 如果正序遍历背包,会重复放入物品,因为利用了新值
// 如果先遍历背包,再遍历物品,相当于背包里只放入了一个物品
for (int i = 0; i < n; i++)
   for (int j = bagweight; j >= 0; j--)
  1. 模拟dp数组
public static void main(String[] args) {
    // 读取输入
    Scanner scanner = new Scanner(System.in);
    int n = scanner.nextInt();
    int bagweight = scanner.nextInt();
    int[] weight = new int[n];
    int[] value = new int[n];
    for (int i = 0; i < n; i++) {
        weight[i] = scanner.nextInt();
    }
    for (int i = 0; i < n; i++) {
        value[i] = scanner.nextInt();
    }
    // dp[j]: 容量为j的背包,所能背的最大价值
    int[] dp = new int[bagweight + 1];
    // 让dp数组在递归公式的过程中取的最大的价值,而不是被初始值覆盖了
    for (int i = 0; i < dp.length; i++) {
        dp[i] = 0;
    }
    // 先正序遍历物品,再倒序遍历背包
    // 如果正序遍历背包,会重复放入物品,因为利用了新值
    // 如果先遍历背包,再遍历物品,相当于背包里只放入了一个物品
    for (int i = 0; i < n; i++) {
        for (int j = bagweight; j >= 0; j--) {
            // 放不进物品i
            if (j < weight[i]) {
                dp[j] = dp[j];
            // 放的进物品i,则有放入物品i价值最大和不放入物品i价值最大
            } else {
                dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]);
            }
            // 其实可以简化,如果都放不进物品i了,循环都没必要到0,只到weight[i]即可
            // 因为都不用更新滚动数组的值,滚动数组的值就是二维数组上一行的值
        }
    }
    System.out.println(dp[bagweight]);
}

416. 分割等和子集

题目讲解:代码随想录
重点:
把这道题转化成01背包问题
思路:

  1. dp数组的含义
// 容量为j的背包能放得最大数值和
int[] dp = new int[target + 1];
  1. 递推公式
// 放入数字i和不放入数字i
dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
  1. dp数组的初始化
// 因为递推公式取的是max
dp[j] = 0
  1. 遍历顺序
// 先正序遍历nums,再反序遍历背包容量
for (int i = 0; i < nums.length; i++)
	for (int j = target; j >= nums[i]; j--)
  1. 模拟dp数组
public boolean canPartition(int[] nums) {
    /*
    分析题目:
    1. 背包容量为sum/2
    2. 每个物品的价值和重量都是他的数值
    3. dp[sum/2]==sum/2则返回true,因为等和的数值的背包能够被装满
    */
    // 计算和及检查存在等和子集的可能
    int sum = 0;
    for (int num : nums) {
        sum += num;
    }
    if (sum % 2 != 0) {
        return false;
    }
    // 背包容量就为等和子集的和
    int target = sum / 2;
    // dp[j] = 容量为j的背包能放得最大数值和
    int[] dp = new int[target + 1];
    // 先正序遍历数字,再倒序遍历背包大小
    for (int i = 0; i < nums.length; i++) {
    	// j小于nums[i]没必要更新,因为都放不进去
        for (int j = target; j >= nums[i]; j--) {
            // 放入数字i和不放入数字i
            dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
        }
        // 剪枝,看看当前取的前i个数字是否能够满足target
        if (dp[target] == target) {
            return true;
        }
    }

    return dp[target] == target;
}

http://www.kler.cn/a/376189.html

相关文章:

  • 项目管理计算公式中的PV、EV、AC、CV、SV、CPI、SPI、ETC、EAC、BAC术语含义
  • Golang | Leetcode Golang题解之第526题优美的排列
  • 制作安装k8s需要的离线yum源
  • npm入门教程19:npm包管理
  • 使用form表单的action提交并接收后端返回的消息
  • 刘艳兵-DBA016-在您的数据库中,SALES表存在于SH用户中,并且启用了统一审计。作为DBA,您成功执行了以下指令:
  • 分布式和微服务系统区别
  • SpringBoot助力大型商场应急预案自动化
  • C语言日记 2024年11月2日
  • 利士策分享,锚定未来:稳健规划人生
  • git reset 删除错误提交
  • 【Python爬虫实战】网络爬虫完整指南:HTTP/HTTPS协议与爬虫安全实践
  • 博物馆3D数字化的优势有哪些?
  • ArcGIS Pro SDK (二十)流图层
  • 【Android】初始路由框架及ARouter原理
  • 基于Matlab GUI的说话人识别测试平台
  • 一般无人机和FPV无人机的区别
  • 使用 MMDetection 实现 Pascal VOC 数据集的目标检测项目练习(二) ubuntu的下载安装
  • 【算法】奇数在偶数后、反转字符串中的单词
  • 仿真工具Modelsim和QuestaSim有什么区别?
  • 摄像机实时接入分析平台LiteAIServer视频智能分析软件诊断噪声检测
  • 利用Claude制作web小游戏(一):俄罗斯方块
  • 【Linux第七课--基础IO】内存级文件、重定向、缓冲区、文件系统、动态库静态库
  • JavaScript 进阶 - 第2天 (黑马笔记)
  • 数字后端零基础入门系列 | Innovus零基础LAB学习Day7
  • 必备!20道大模型面试问题详解(含答案)