01背包:模板题+实战题

news/2024/12/21 20:42:33/

一、01背包的定义

我们有一个背包,背包的容积有限,最多只能装下总体积为V的物品。现在给定我们N个物品,第i个物品的体积vi,对应的价值是wi 1 ≤ i ≤ N 1 \leq i \leq N 1iN)。每个物品有且仅有一个。要求我们再背包容量允许的范围内,选取物品,使得总价值最大。(注意每一个物品要么选,要么不选,这就是 0 1 背包名字的由来

二、模板题

1、题目说明

牛客题目连接
在这里插入图片描述

解题思路

第一问

求这个背包至多能装多大价值的物品?
第一问实际上就是经典的01背包问题。给定一个背包的体积V,在n个不同的物品中选取最大化的价值。(对于一个物品,只有选或者不选两种可能

  1. 状态表示
       int n=in.nextInt();//物品数量int V=in.nextInt();//背包的总体积//dp[i][j]前i个物品中选择,累计总体积不大于j获取的最大价值int[][] dp=new int[n+1][V+1];
  1. 状态转移方程(dp[i][j]的状态转移)
       //不选第i个物品 dp[i][j]=dp[i-1][j]; //(体积j是不变的如果不选第i个物品)//选第i个物品(注意如果选第i个物品,必须j>=v[i],即当前体积j必须保证能够放进体积为v[i]的物品!!!)dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]);
  1. 初始化dp数组
    根据状态转移方程,我们只用初始化好上一行,也就是dp[0][j]即可。
    dp[0][j]表示在前0个物品中选择体积不大于j的最大总价值,显然0个物品,自然最大价值是0,所以dp[0][j]初始化成0,即dp二维数组的第0行初始化成0

  2. 返回值
    直接打印,dp[n][V]:前n个物品中选取,容量不大于背包的总容量V的最大价值。

  3. AC代码_Java

import java.util.Scanner;// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int n = scanner.nextInt();int V = scanner.nextInt();//dp[i][j]表示容量不大于j,最大价值是iint[][] dp = new int[n + 1][V + 1];//第i个物品的容量(1开始)int[] v = new int[n + 1];//第i个物品的价值(1开始)int[] w = new int[n + 1];//获取每个物品的容量、价值for (int i = 1; i <= n; i++) {v[i] = scanner.nextInt();w[i] = scanner.nextInt();}//第一道题目的转移方程//不选第i个物品 dp[i][j]=dp[i-1][j];//选第i个物品(j-v[i]>=0才允许)dp[i][j]=dp[i-1][j-v[i]]+w[i]//第一道题目不用初始化(自动是0)//返回值就是dp[n][V];for (int i = 1; i <= n; i++) {for (int j = 1; j <= V; j++) {dp[i][j] = dp[i - 1][j];if (j - v[i] >= 0)dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - v[i]] + w[i]);}}System.out.println(dp[n][V]);}
}

第二问

若背包恰好装满,求至多能装多大价值的物品?
注意请先了解了第一问的做法,再来看第二问,因为第二问是基于第一问演化过来的。
这个题目其实只需要稍微修改一下状态表示即可。之前的状态表示:
dp[i][j]前i个物品中选择,累计总体积不大于j获取的最大价值
当前我们要求恰好装满背包,能装的最大价值,那么状态表示可以修改成:
dp[i][j]前i个物品中选择,累计总体积恰好是j获取的最大价值
值得注意的是,有时可能凑不出恰好为j体积的情况,怎么办呢?既然凑不出,我们可以直接将其赋值成-1,代表这种状态不可能出现。

随即,我们还要修改一下转移方程其实和第一问大差不差:

  //不选第i个物品 dp[i][j]=dp[i-1][j];//选第i个物品,前一个状态必须能够凑齐对应的体积!!!if(j-v[i]>=0&&dp[i-1][j-v[i]]!=-1)dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);

最终AC代码_Java

import java.util.Scanner;// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int n = scanner.nextInt();int V = scanner.nextInt();//dp[i][j]表示容量不大于j,最大价值是iint[][] dp = new int[n + 1][V + 1];//第i个物品的容量(1开始)int[] v = new int[n + 1];//第i个物品的价值(1开始)int[] w = new int[n + 1];//获取每个物品的容量、价值for (int i = 1; i <= n; i++) {v[i] = scanner.nextInt();w[i] = scanner.nextInt();}//第一道题目的转移方程//不选第i个物品 dp[i][j]=dp[i-1][j];//选第i个物品(j-v[i]>=0才允许)dp[i][j]=dp[i-1][j-v[i]]+w[i]//第一道题目不用初始化(自动是0)//返回值就是dp[n][V];for (int i = 1; i <= n; i++) {for (int j = 1; j <= V; j++) {dp[i][j] = dp[i - 1][j];if (j - v[i] >= 0)dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - v[i]] + w[i]);}}System.out.println(dp[n][V]);//把dp表清空for (int i = 1; i <= n; i++) {for (int j = 1; j <= V; j++) {dp[i][j] = 0;}}for (int j = 1; j <= V; j++)dp[0][j] = -1;//第二题记得初始化,价值一直是0,那么除了00 其他-1//第二道题目的转移方程(选i个物品,恰好容量达到j的最大价值)规定dp如果是-1,代表这种情况不可能发生//不选第i个物品 dp[i][j]=dp[i-1][j];//选第i个物品 (j-v[i]>=0&&dp[i-1][j-v[i]]!=-1才允许)dp[i][j]=dp[i-1][j-v[i]]+w[i]for (int i = 1; i <= n; i++) {for (int j = 1; j <= V; j++) {dp[i][j] = dp[i - 1][j];if (j - v[i] >= 0&&dp[i-1][j-v[i]]!=-1)dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - v[i]] + w[i]);}}System.out.println(dp[n][V]==-1?0:dp[n][V]);}
}

三、实战题

题目

蓝桥云课题目连接
在这里插入图片描述

解题思路

乍一看,好像没有思路,它让我们求平分,和我01背包有什么关系?我们其实可以换一个角度思考。我们先计算所有宝藏的总价值sumsum/2是奇数,直接打印no。因为题目说了,每个宝藏是不能被拆分的,只能拿,或者不拿。
如果不是奇数,我们把sum/2想象成01背包的背包容量(即宝藏总价值的一半)。每个宝藏想象成一个个物品,宝藏的价值就是物品的价值。那么在容量不大于sum/2的情况下取获取物品,使得选取的总价值恰好等于sum/2不就说明可以平分吗?
于是通过这个思路,我们就把这个题转化成了模板题中的第二问!

Java_184">AC_Code_Java

   import java.util.*;public class Main {public static void main(String[] args) {Scanner scan = new Scanner(System.in);int n=scan.nextInt();int[] arr=new int[n+1];int sum=0;for(int i=1;i<=n;i++){arr[i]=scan.nextInt();//第i个宝藏(物品)的价值sum+=arr[i];}scan.close();if(sum%2==1){System.out.print("no");return;}//sum一定是偶数//dp[i][j]前i个物品中选,体积恰好是j的最大价值int[][] dp=new int[n+1][sum/2+1];dp[0][0]=0;for(int i=1;i<=sum/2;i++)dp[0][i]=-1;//0个物品,凑不出大于0的价值,所以设置成无效值//填写dp表for(int i=1;i<=n;i++){for(int j=1;j<=sum/2;j++){dp[i][j]=dp[i-1][j];if(j>=arr[i]&&dp[i-1][j-arr[i]]!=-1){dp[i][j]=Math.max(dp[i][j],dp[i-1][j-arr[i]]+arr[i]);}}}System.out.print(dp[n][sum/2]==-1?"no":"yes");}}

http://www.ppmy.cn/news/1557011.html

相关文章

【JavaWeb】Ajax

目录 一、什么是Ajax&#xff1f; 二、同步与异步 三、Ajax工作原理 四、Ajax实现步骤 五、Ajax应用场景 六、Ajax常见问题 1.缓存问题 2.跨域问题 3.请求超时与网络异常 4.取消请求 七、常见Ajax三种请求方式 1.jQuery请求 2.Axios请求 3.Fetch请求 一、什么是A…

mac编译ijkplayer遇到问题

问题&#xff1a;./init-android.sh git version 2.44.0 pull ffmpeg base : command not founde.sh: line 2: : command not founde.sh: line 5: : command not founde.sh: line 6: tools/pull-repo-base.sh: line 9: syntax error near unexpected token elif ools/pull-re…

【第九节】Git 服务器搭建

目录 前言 一、 使用裸存储库搭建 Git 服务器 1.1 安装 Git 1.2 创建裸存储库 1.3 配置 SSH 访问 1.4 克隆仓库 二、 使用 GitLab 搭建 Git 服务器 2.1 安装 GitLab 2.2 配置 GitLab 2.3 创建项目 2.4 生成 SSH 密钥 2.5 添加 SSH Key 三、 使用 GitLab 管理项目 …

智源大模型通用算子库FlagGems四大能力升级 持续赋能AI系统开源生态

FlagGems是由智源研究院于2024年6月推出的面向多种AI芯片的开源大模型通用算子库。FlagGems使用Triton语言开发&#xff0c;在Triton生态开源开放的基础上&#xff0c;为多种AI芯片提供开源、统一、高效的算子层生态接入方案。FlagGems沿着统一的中间语言、统一的算子接口和统一…

条款24:若所有参数皆需类型转换,请为此采用非成员函数

条款24&#xff1a;若所有参数皆需类型转换&#xff0c;请为此采用非成员函数 设计一个表示有理数的类时&#xff0c;允许从整数隐式转换为有理数是有用的&#xff1a; class Rational { public:Rational(int numerator 0, // 该构造函数没有explicit限制;int denominator …

linux下操作es及kibana的操作记录

背景&#xff1a;工作中后面开始用es和kibana了&#xff0c;为了方便后面的操作&#xff0c;特记录一下&#xff0c;好多命令实在是记不住了&#xff0c;&#x1f604; kibana的操作 1.查看所有的索引的命令 GET /_cat/indices2.创建索引的命令 PUT /es_dsj_6c_jky_yunzhe_…

【机器学习】机器学习的基本分类-强化学习-REINFORCE 算法

REINFORCE 算法 REINFORCE 是一种基于策略梯度的强化学习算法&#xff0c;直接通过采样环境中的轨迹来优化策略。它是策略梯度方法的基础实现&#xff0c;具有简单直观的优点。 核心思想 目标函数 最大化策略的期望回报&#xff1a; ​​​​​​​ …

SSL Version 2 and 3 Protocol Detection漏洞修复

使用 IIS Crypto 工具 IIS Crypto 是一个免费工具&#xff0c;使管理员能够在 Windows Server 2008&#xff0c;2012&#xff0c;2016 和 2019 上启用或禁用协议&#xff0c;密码&#xff0c;哈希和密钥交换算法。它还允许您重新排序 IIS 提供的 SSL / TLS 密码套件&#xff0c…