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

news2024/11/18 23:44:15

系列文章目录


目录

  • 系列文章目录
  • 动态规划: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.coloradmin.cn/o/1657638.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【MySQL基本查询(上)】

文章目录 一、多行插入 指定列插入数据更新表中某个数据的信息&#xff08;on duplicate&#xff09;了解affected报告信息 二、检索功能1.select 查询1.1全列查询1.2指定列查询1.3where条件筛选子句案例 2.结果排序案例 3.筛选分页结果offset实现分页 一、多行插入 指定列插…

QT:小项目:登录界面 (下一章连接数据库)

一、效果图 登录后&#xff1a; 二、项目工程结构 三、登录界面UI设计 四主界面 四、源码设计 login.h #ifndef LOGIN_H #define LOGIN_H#include <QDialog>namespace Ui { class login; }class login : public QDialog {Q_OBJECTpublic:explicit login(QWidge…

es使用遇到的bug总结

本来版本7.4.0不行&#xff0c;最后换了个版本7.15.1就可以了&#xff0c;但又出现以下问题了&#xff1a; Beanpublic ElasticsearchClient elasticsearchClient() { // RestClient client RestClient.builder(new HttpHost("localhost", 9200,"http&q…

STM32理论 —— μCOS-Ⅲ(新)

文章目录 1. 任务调度器1.1 抢占式调度 μCos-Ⅲ全称是Micro C OS Ⅲ&#xff0c;由Micriμm 公司发布的一个基于C 语言编写的第三代小型实时操作系统(RTOS)&#xff1b; RTOS 与裸机相比最大的优势在于多任务管理与实时性&#xff0c;它提供了多任务管理和任务间通信的功能&a…

找出100~200的全部素数

解题思路&#xff1a; 判别 m 是否为素数的算法是这样的&#xff1a;让 m 被2~除&#xff0c;如果 m 不能被2~之中任何一个整数整除&#xff0c;就可以确定 m 是素数。为了记录 m 是否为素数&#xff0c;可以用一个布尔变量 prime 来表示。在循环开始时先设 prime 为真…

【Leetcode】八大排序

总述 插入排序&#xff1a;直接插入排序&#xff1b;希尔排序&#xff1b; 选择排序&#xff1a;简单选择排序&#xff1b;堆排序&#xff1b; 交换排序&#xff1a;冒泡排序&#xff1b;快速排序&#xff1b; 归并排序&#xff1b; 桶排序/基数排序&#xff1b; 直接插入排序 …

ssm105基于JAVAEE技术校园车辆管理系统+jsp

校园车辆管理系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本校园车辆管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短…

IOS Xcode证书配置和ipa打包流程(附详细图文教程)

IOS Xcode证书配置和ipa打包流程&#xff08;附图文教程&#xff09; 前言ipa文件简介证书文件简介Provisioning Profile描述文件简介当前环境版本Xcode证书配置和ipa打包流程生成Apple Distribution Certificates证书创建描述文件&#xff08;Provisioning Profiles&#xff0…

【python】模块与包

Python中的模块和包是组织和管理代码的重要工具。通过模块和包&#xff0c;你可以更好地管理和重用你的代码&#xff0c;使得代码更加模块化和可维护。 目录 前言 正文 一、模块 1、模块的分类 1&#xff09;内置模块 python解释器中默认拥有的模块可以直接使用&#xff08;…

守护数字疆域:2024年网络安全报告深度解读

在这个数据如潮涌动的数字时代&#xff0c;每一比特信息都可能是攻防双方角力的战场。《Check Point 2024年网络安全报告》不但为我们揭示了过去一年网络安全世界的风云变幻&#xff0c;更以前瞻性的视角勾勒出未来的挑战与机遇。此刻&#xff0c;让我们携手深潜这份权威指南的…

Offer必备算法37_记忆化搜索_五道力扣题详解(由易到难)

目录 记忆化搜索概念和使用场景 ①力扣509. 斐波那契数 解析代码1_循环 解析代码2_暴搜递归 解析代码3_记忆化搜索 解析代码4_动态规划 ②力扣62. 不同路径 解析代码1_暴搜递归&#xff08;超时&#xff09; 解析代码2_记忆化搜索 解析代码3_动态规划 ③力扣300. 最…

对时间序列异常检测的严格评价

论文地址&#xff1a;https://ojs.aaai.org/index.php/AAAI/article/view/20680 论文源码&#xff1a;无 会议&#xff1a;The Thirty-Sixth AAAI Conference on Artificial Intelligence 这篇论文名为《Towards a Rigorous Evaluation of Time-Series Anomaly Detection》&a…

Java毕设之基于SpringBoot的在线拍卖系统

运行环境 开发语言:java 框架:springboot&#xff0c;vue JDK版本:JDK1.8 数据库:mysql5.7(推荐5.7&#xff0c;8.0也可以) 数据库工具:Navicat11 开发软件:idea/eclipse(推荐idea) 系统详细设计 管理员功能模块 管理员登录&#xff0c;管理员通过输入用户名、密码、角色等信…

网络新手如何上手水牛社软件?我的建议与看法

水牛社是一款专为电脑用户设计的软件&#xff0c;拥有明确的著作权&#xff0c;其核心功能在于发布和整合各类网络活动任务资源、教程等&#xff0c;内容多元且不设固定分类。其靠谱程度取决于你对软件的了解程度和个人需求的适配性。 软件内部包含五个主要栏目&#xff0c;大…

前端开发攻略---打破Chrome的最小字号限制,设置任意字号大小

目录 1、原因 2、解决方法 1、原因 由于Chrome浏览器的限制&#xff0c;在网页中的字号默认最小为12px&#xff0c;更改为12px以下的字号大小是无效的 2、解决方法 1、在Chrome浏览器中调整字号最小值 优点&#xff1a;快&#xff0c;方便&#xff0c; 缺点&#xff1a;只对自…

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

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

python:画折线图

import pandas as pd import matplotlib.pyplot as plt from matplotlib.font_manager import FontProperties# 设置新宋体字体的路径 font_path D:/reportlab/simsun/simsun.ttf# 加载新宋体字体 prop FontProperties(fnamefont_path)""" # 读取 xlsx 文件 d…

API低代码平台介绍2-最基本的数据查询功能

最基本的数据查询功能 本篇文章我们将介绍如何使用ADI平台定义一个基本的数据查询接口。由于是介绍平台具体功能的第一篇文章&#xff0c;里面会涉及比较多的概念介绍&#xff0c;了解了这些概念有助于您阅读后续的文章。 ADI平台的首页面如下&#xff1a; 1.菜单介绍 1.1 O…

交友软件源码-源码+搭建+售后,上线即可运营聊天交友源码 专业语聊交友app开发+源码搭建-快速上线

交友小程序源码是一种可以帮助开发者快速搭建交友类小程序的代码模板。它通常包括用户注册、登录、个人信息编辑、匹配推荐、好友聊天等常见功能&#xff0c;以及与后台数据交互的接口。使用这种源码可以极大地缩短开发时间&#xff0c;同时也可以根据自己的需求进行二次开发和…

代码随想录算法训练营DAY45|C++动态规划Part7|70.爬楼梯(进阶版)、322. 零钱兑换、279.完全平方数

文章目录 70.爬楼梯&#xff08;进阶版&#xff09;⭐️322. 零钱兑换思路CPP代码总结 279.完全平方数思路CPP代码 70.爬楼梯&#xff08;进阶版&#xff09; 卡码网&#xff1a;57. 爬楼梯 文章讲解&#xff1a;70.爬楼梯(进阶版) 本题就是典型了完全背包排列问题&#xff0c;…