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

系列文章目录


目录

  • 系列文章目录
  • 动态规划:01背包理论基础
    • ①二维数组
    • ②一维数组(滚动数组)
  • 416. 分割等和子集
    • ①回溯法(超时)
    • ②动态规划(01背包)
      • 未剪枝版
      • 剪枝版


动态规划:01背包理论基础

(1)输入读取方法:

  1. Scanner sc = new Scanner(System.in);
            String str = sc.nextLine();
            int m = Integer.parseInt(str.split(" ")[0]);
            int n = Integer.parseInt(str.split(" ")[1]);
            //将String[]数组通过stream流转换成int[]数组
            int[] weights = Arrays.stream(sc.nextLine().split(" ")).mapToInt(/*s->Integer.parseInt(s)*/Integer::parseInt).toArray();
            int[] values =
                    Arrays.stream(sc.nextLine().split(" ")).mapToInt(new ToIntFunction<String>() {
                        @Override
                        public int applyAsInt(String value) {
                            return Integer.parseInt(value);
                        }
                    }).toArray();
    
  2. Scanner sc = new Scanner(System.in);
            
            // 读取背包容量和物品数量
            int m = sc.nextInt();
            int n = sc.nextInt();
            sc.nextLine(); // 消耗掉输入缓冲区的换行符
            
            // 读取物品重量和价值
            int[] weights = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
            int[] values = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
    
  3. // 获取输入数据
            Scanner sc = new Scanner(System.in);
            int m = sc.nextInt();
            int n = sc.nextInt();
            
            int[] weights = new int[m];
            for (int i = 0; i < m; i++){
                weights[i] = sc.nextInt();
            }
            
            int[] values = new int[m];
            for (int i = 0; i < m; i++){
                values[i] = sc.nextInt();
            }
    

①二维数组

(1)确定dp数组及其含义:
表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
(2)确定递推关系

  • 容量不够:一定放不下,直接返回不放 i 的最大价值。
  • 容量够:根据两种方案的价值做选择,选价值大的。
    • 不放i:相当于在 0 ~ (i-1) 件物品中选择,容量不变;
    • i:在确定放 i 的前提下(腾出空间给 i ),获取背包能产生的最大价值,再加上 i 的价值。

(3)考虑初始化
初始化第一行:对应物品0,如果背包容量不够,则设置为0,如果够,则设置为values[0]
初始化第一列:对应背包容量0,则无论是什么物品都放不下,不能产生任何价值,直接为默认值0即可。

代码如下:

import java.util.Arrays;
import java.util.Scanner;
import java.util.function.ToIntFunction;

public class BagProblem {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String str = sc.nextLine();
        int m = Integer.parseInt(str.split(" ")[0]);
        int n = Integer.parseInt(str.split(" ")[1]);
        //将String[]数组通过stream流转换成int[]数组
        int[] weights = Arrays.stream(sc.nextLine().split(" ")).mapToInt(/*s->Integer.parseInt(s)*/Integer::parseInt).toArray();
        int[] values =
                Arrays.stream(sc.nextLine().split(" ")).mapToInt(new ToIntFunction<String>() {
                    @Override
                    public int applyAsInt(String value) {
                        return Integer.parseInt(value);
                    }
                }).toArray();
        //确定dp数组下标及含义:dp[i][j] 表示从下标为0-i的物品里任取,放到容量为j的背包中,价值总和最大为多少
        int[][] dp = new int[m][n+1];//需要考虑容量和物品数量为0的情况
        //dp数组初始化
        for (int i = 0; i < m; i++) {//列初始化
            dp[i][0] = 0;
        }
        for (int j = weights[0]; j <= n; j++) {//行初始化
            dp[0][j] = values[0];
        }
        //确定遍历顺序(先遍历物品再遍历容量或者先遍历容量再遍历背包都行)
        //①先遍历物品再遍历容量
        for (int i = 1; i < m; i++) {
            for (int j = 1; j <= n; j++) {
                /**
                 * 当前背包的容量都没有当前物品i大的时候,是不放物品i的
                 * 那么前i-1个物品能放下的最大价值就是当前情况的最大价值
                 */
                if(j<weights[i]){
                    dp[i][j] = dp[i - 1][j];
                }else {
                    /**
                     * 当前背包的容量可以放下物品i
                     * 那么此时分两种情况:
                     *    1、不放物品i
                     *    2、放物品i
                     * 比较这两种情况下,哪种背包中物品的最大价值最大
                     */
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weights[i]] + values[i]);
                }
            }
        }
        System.out.println(dp[m-1][n]);
    }
}

②一维数组(滚动数组)

(1)确定dp数组及其含义:
在一维dp数组中,dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]
(2)确定递推关系

  • 容量不够: dp[j] ,不放物品i
  • 容量够:根据两种方案的价值做选择,选价值大的。
    • 不放idp[j] ,相当于在 0 ~ (i-1) 件物品中选择,容量不变;
    • idp[j - weight[i]] + value[i],在确定放 i 的前提下(腾出空间给 i ),获取背包能产生的最大价值,再加上 i 的价值。

(3)考虑初始化
dp[0]=0,因为背包容量为0所背的物品的最大价值就是0。那么dp数组除了下标0的位置,初始为0,其他下标应该初始化多少呢?看一下递归公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]); dp数组在推导的时候一定是取价值最大的数,如果题目给的价值都是正整数那么非0下标都初始化为0就可以了。

import java.util.Arrays;
import java.util.Scanner;
public class BagProblem {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int m = sc.nextInt();
        int n = sc.nextInt();
        sc.nextLine();//接收换行符
        int[] weights = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
        int[] values = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();

        //确定dp数组及含义(背包容量为j的背包所能装的最大价值
        int[] dp = new int[n + 1];
        //dp数组初始化
        dp[0] = 0;//当背包容量为0时,最大价值也为0
        for (int i = 0; i < m; i++) {//遍历物品
            for (int j = n; j >= 0; j--) {//遍历容量(倒序遍历)
                if (j < weights[i]) {
                    dp[j] = dp[j];
                } else {
                    dp[j] = Math.max(dp[j], dp[j - weights[i]] + values[i]);
                }
            }
        }
        System.out.println(dp[n]);
    }
}

416. 分割等和子集

①回溯法(超时)

import java.util.Arrays;

public class SplitEqualSumSubsets {
    public static void main(String[] args) {
        int[] nums = {3,3,3,4,5};
        Solution solution = new Solution();
        boolean answer = solution.canPartition(nums);
        System.out.println(answer);
    }
}

class Solution {
    int sum = 0;
    int tempSum = 0;

    public boolean canPartition(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
        }
        if (sum % 2 != 0) return false;//如果总和为奇数,则无法分割为两个等和子集
        //对数组从小到大排序
        Arrays.sort(nums);
        return backTracking(nums, 0);
    }

    public boolean backTracking(int[] nums, int startIndex) {//确定回溯函数的参数及返回值
        //确定回溯函数终止条件
        if (tempSum == sum / 2) return true;
        if (tempSum > sum / 2) {
            return false;
        }
        //确定单层递归逻辑
        boolean answer1 = false;
        for (int i = startIndex; i < nums.length; i++) {
            tempSum += nums[i];
            answer1 = backTracking(nums, i + 1);
            if(answer1)return true;// 如果找到一个可行解,立即返回,不再往下遍历
            tempSum -= nums[i];//回溯
        }
        return answer1;
    }
}

②动态规划(01背包)

未剪枝版

class Solution {
    public boolean canPartition(int[] nums) {
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
        }
        //总和为奇数,不能平分
        if (sum % 2 != 0) return false;
        //确定dp数组含义(容量为j的背包,放进0~i任意物品后,背的最大重量。
        int target = sum / 2;
        int[] dp = new int[target + 1];
        //dp数组初始化
        dp[0] = 0;
        for (int i = 0; i < nums.length; i++) {//先遍历物品
            for (int j = target; j >= 0; j--) {//倒序遍历背包容量
                if (j < nums[i]) {
                    dp[j] = dp[j];
                } else {
                    dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
                }
                //System.out.print(dp[j]);
            }
            //System.out.println();
        }
        return dp[target] == target;//如果背包装满了,即能找到等和子集
    }
}

剪枝版

class Solution {
    public boolean canPartition(int[] nums) {
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
        }
        //总和为奇数,不能平分
        if (sum % 2 != 0) return false;
        //确定dp数组含义(容量为j的背包,放进0~i任意物品后,背的最大重量。
        int target = sum / 2;
        int[] dp = new int[target + 1];
        //dp数组初始化
        dp[0] = 0;
        for (int i = 0; i < nums.length; i++) {//先遍历物品
            for (int j = target; j >= 0; j--) {//倒序遍历背包容量
                if (j < nums[i]) {
                    dp[j] = dp[j];
                } else {
                    dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
                }
                //System.out.print(dp[j]);
            }
            //System.out.println();
            //剪枝一下,每一次完成内层的for-loop,立即检查是否dp[target] == target,优化时间复杂度
            if (dp[target] == target) return true;
        }
        return dp[target] == target;//如果背包装满了,即能找到等和子集
    }
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/608398.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

渗透之sql注入----二次注入

目录 二次注入的原理&#xff1a; 实战&#xff1a; 第一步&#xff1a;找注入点 找漏洞&#xff1a; 注入大概过程&#xff1a; 第二步&#xff1a;开始注入 二次注入的原理&#xff1a; 二次注入是由于对用户输入的数据过滤不严谨&#xff0c;导致存在异常的数据被出入…

FreeRTOS的任务详解、创建与删除

目录 1、任务详解 1.1 什么是任务&#xff1f; 1.2 任务的特点 1.3 任务的状态 1.4 任务的优先级 1.5 任务的堆和栈 2、任务的创建与删除 2.1 相关API 2.2 函数解析 2.2.1 xTaxkCreate() 2.2.2 xTaskCreateStatic() 2.2.3 vTaskDelete() 3、实战案例 3.1 创建两个…

达梦数据刷盘测试

达梦数据库为了保证数据故障恢复的一致性&#xff0c;REDO 日志的刷盘必须在数据页刷盘之前进行。 下面我们通过测试来验证是不是这样 执行我们事先准备的SHELL脚本 可以看到第一次strings文件没有输出&#xff0c;说明刚写的数据在数据库的BUFFER缓冲区内&#xff0c;还没有刷…

RN阴影组件使用

yarn add react-native-shadow yarn add react-native-svg // 这个是必须的,阴影依赖这个包四周都有阴影,如下设置 import React from react; import {StyleSheet, View, Text} from react-native; import {BoxShadow} from react-native-shadow;const App () > {const …

3GBJ5016A-ASEMI电焊机专用3GBJ5016A

编辑&#xff1a;ll 3GBJ5016A-ASEMI电焊机专用3GBJ5016A 型号&#xff1a;3GBJ5016A 品牌&#xff1a;ASEMI 封装&#xff1a;3GBJ-5 正向电流&#xff08;Id&#xff09;&#xff1a;50A 反向耐压&#xff08;VRRM&#xff09;&#xff1a;1600V 正向浪涌电流&#xf…

“找不到mfcm80u.dll”错误怎么办?一文了解原因和解决办法!

在使用Windows操作系统时&#xff0c;许多用户可能会遇到各种DLL文件缺失或损坏的问题。其中&#xff0c;“找不到mfc80u.dll”错误就是比较常见的一种。 下面小编就给大家分享出现“找不到mfc80u.dll”错误的原因和解决办法&#xff0c;帮助您快速解决此问题。 一、mfc80u.dl…

漏洞管理是如何在攻击者之前识别漏洞从而帮助人们阻止攻击的

漏洞管理 是主动查找、评估和缓解组织 IT 环境中的安全漏洞、弱点、差距、错误配置和错误的过程。该过程通常扩展到整个 IT 环境&#xff0c;包括网络、应用程序、系统、基础设施、软件和第三方服务等。鉴于所涉及的高成本&#xff0c;组织根本无法承受网络攻击和数据泄露。如果…

mysql数据库调优篇章1

目录 1.认识数据库中日志的作用2.增加mysql数据库中my.ini 基本配置3.增加my.ini中参数配置4.查看已经执行过的sql语句过去执行时间5.找出慢查询的sql6. SHOW VARIABLES LIKE ‘innodb_read_io_threads’; SHOW VARIABLES LIKE ‘innodb_write_io_threads’; SHOW VARIABLES LI…

森林消防—高扬程水泵,高效、稳定、可靠!/恒峰智慧科技

森林&#xff0c;作为地球的“绿色肺叶”&#xff0c;不仅为我们提供了丰富的自然资源&#xff0c;更是维持生态平衡的重要一环。然而&#xff0c;随着全球气候的变化和人为活动的增加&#xff0c;森林火灾频发&#xff0c;给生态环境和人民生命财产安全带来了巨大威胁。在森林…

17 空闲空间管理

目录 假设 底层机制 分割与合并 追踪已分配空间的大小 嵌入空闲列表 让堆增长 基本策略 最优匹配 首次匹配 下次匹配 其他方式 分离空闲列表 伙伴系统 小结 分页是将内存成大小相等的内存块&#xff0c;这样的机制下面&#xff0c;很容易去管理这些内存&#xff0c…

代码随想录Day 37|Leetcode|Python|● 1049. 最后一块石头的重量 II ● 494. 目标和 ● 474.一和零

1049. 最后一块石头的重量 II 有一堆石头&#xff0c;用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。假设石头的重量分别为 x 和 y&#xff0c;且 x < y。那么粉碎的可能结…

【C++】详解STL容器之一的deque和适配器stack,queue

目录 deque的概述 deque空间的结构 deque的迭代器 deque的数据设计 deque的优缺点 适配器的概念 ​编辑 stack的概述 stack的模拟实现 queue的概述 queue的模拟实现 deque的概述 deque的设计参考了另外两大容器vector和list。可参考下面两篇文章 详解vector&#x…

Java 语法 (杂七杂八的知识)

面向对象三大特性 封装, 多态, 继承 基本数据类型 一字节 (Byte) 占八位 (bit) JDK, JRE, JVM JDK (Java Development Kit) : Java 开发工具包, 包括了 JRE, 编译器 javac, 和调试工具 Jconsole, jstack 等 JRE (Java Runtime Environment) : Java 运行时环境, 包括了 JVM , …

ssm115乐购游戏商城系统+vue

毕业生学历证明系统 设计与实现 内容摘要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统毕业生学历信息管理难…

【Linux系统】进程控制

再次理解进程 进程&#xff1a;内核的相关管理数据结构(task_struct(进程控制块PCB)&#xff0c;mm_struct(地址空间)&#xff0c;页表) 代码和数据 那么如何理解进程具有独立性&#xff1f; 我们之前已经学习过进程控制块啊&#xff0c;地址空间啊&#xff0c;页表啊&…

什么是期货?期货的基础知识有哪些?

期货是一种标准化的远期合约&#xff0c;允许买卖双方在未来特定时间以预定价格交易货物或金融资产。也是一种金融衍生品&#xff0c;它为市场参与者提供了一种管理价格波动风险和进行投资的工具。 期货的基础知识有哪些 期货市场是一个复杂的金融环境&#xff0c;对于初学者来…

程序猿敲代码费脑掉头发?来看看铁打的便捷,Baidu Comate智能代码助手

前言&#xff1a;Baidu Comate 前世今生 Baidu Comate 安装教程 官网安装教程 手动安装教程 登录使用 插件功能初体验 代码生成指令板块 简易代码生成 代码解释 代码补充 代码注释 多种类智能问答&知识集调用 Paddle团队官方知识集 前言&#xff1…

设计模式(2)——工厂方法模式

目录 1. 摘要 2. 需求案例(设计一个咖啡店的点餐系统) 2.1 咖啡父类及其子类 2.2 咖啡店类与咖啡类的关系 3. 普通方法实线咖啡店点餐系统 3.1 定义Coffee父类 3.2 定义美式咖啡类继承Coffee类 3.3 定义拿铁咖啡继承Coffee类 3.4 定义咖啡店类 3.5 编写测试类 4. 简…

影响视频视觉质量的因素——各类视觉伪影

模糊效应&#xff08;Blurring Artifact&#xff09; 图像模糊&#xff08;blurring&#xff09;&#xff1a;平滑图像的细节和边缘产生的现象&#xff0c;模糊对于图像来说&#xff0c;是一个低通滤波器&#xff08;low-pass filter&#xff09;。一般而言&#xff0c;用户更…

VisualGDB:Linux静态库项目创建、编译及库的使用

接上篇《VisualGDB&#xff1a;Linux动态库项目创建、编译及库的使用》&#xff0c;静态库的创建和使用与动态库基本无差别&#xff0c;唯一需要做的就是指定项目生成静态库。 一、指定项目生成静态库 二、重新构建和编译项目 这里注意&#xff0c;同样要copy一个libxxx.so格式…
最新文章