DP(4) | 0-1背包 | Java | LeetCode 1049, 494, 474 做题总结

news2024/9/23 13:20:16

1049. 最后一块石头的重量 II

和 LC 416.分割等和子集 类似

  • 思路(我没有思路):
    两块石头相撞,这里没有想到的一个点是,相撞的两个石头要几乎相似
    以示例1为例,stones = [2,7,4,1,8,1],如果从左到右相撞,得到的不是最优结果
    最优结果是相撞后重量最小的

    stones = [2,7,4,1,8,1],总和23,一半为11,把这组石头分为一组11 一组12,他们相撞就是1

    0-1背包,M个物品放入容量为N的背包中,物品价值value、重量weight

递归五步:

  1. 确定dp数组(dp table)以及下标的含义
    背包重量 j 最大价值 dp[j] 最大重量 dp[j]
  2. 确定递推公式
    价值: dp[j] = Math.max(dp[j], dp[j-weight[i]] +value[i])
    重量:dp[j] = Math.max(dp[j], dp[j-stones[i]] +stones[i])
  3. dp数组如何初始化
    dp[0] = 0,其余也都是0
    ② 由于题目定义 1 <= stones.length <= 30; 1 <= stones[i] <= 100 所以极端情况下每个元素都为100,共30个元素,sum = 3000 , half = 1500
    int[]dp = new int [1501]
  4. 确定遍历顺序
    for物品 { for背包 }
  5. 举例推导dp数组
    求得一组石头的重量是 a = dp[target] ,另一组为 b= sum-dp[target]
    这里 b-a一定大于零,因为sum/2是向下取整的
  • ac-二维数组版本
    写的时候出错:① 递推公式写错 ② 初始化行列不分 ③ 初始化的值应为固定的value[0]
    这道题目的二维数据版本空间内存消耗很多。
class Solution {
    public int lastStoneWeightII(int[] stones) {
        int sum = 0;
        for(int i=0; i<stones.length; i++) {
            sum += stones[i];
        }
        int bagSize = sum/2; // N
        // M = stones.length
        int[][] dp = new int[stones.length][bagSize+1]; //二维数组空间多很多

        //初始化第0行,物品0从哪里开始放
        for(int i=stones[0]; i<bagSize+1; i++) { // weight[0]
            dp[0][i] = stones[0];//value[0]错了
        }

        for(int i=1; i<stones.length; i++) {
            for(int j=1; j<bagSize+1; j++) {
                if(j<stones[i]) {
                    dp[i][j] = dp[i-1][j];
                } else {
                    dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-stones[i]] + stones[i] );
                }
            }
        }
        return sum-2*dp[stones.length-1][bagSize];
    }
}

在这里插入图片描述

  • ac-滚动数组版本
    写的过程中:递推公式又错了。
class Solution {
    public int lastStoneWeightII(int[] stones) {
        int sum = 0;

        for(int i=0; i<stones.length; i++) {
            sum += stones[i];
        }

        int bagSize = sum/2; // N
        int M = stones.length; // M
        
        int[] dp = new int[bagSize+1];
        for(int i=0; i<M; i++) {
            for(int j=bagSize; j>=stones[i]; j--) { // weight[i]
            //两种情况,要么放,要么不放
                dp[j] = Math.max(dp[j],dp[j-stones[i]]+stones[i]); // 1 weight 2 value
            }
        }

        return sum-2*dp[bagSize];
    }
}

494. 目标和

思路

准备两个背包,一个背包package_a存放标记为正的元素,另一个背包package_b存放标记为负的元素。package_a - package_b = target

设nums的元素和为sum, 可以列出方程:

package_a - package_b = target;
package_a + package_b = sum;

所以 package_a = (target + sum)/2。 所以根据题意给的target和sum,我们可以求出package_a的值。

那这道题就可以转化为:给定一个大小为package_a的背包,有多少种组合方式能把背包装满? 妥妥的0-1背包。

元素放或者不放

答案

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        for(int i=0; i<nums.length; i++) {
            sum += nums[i];
        }        

        if(sum < Math.abs(target)){
            return 0;
        }

        if((sum + target) % 2 != 0) {
            return 0;
        }


        int bagSize = (target+sum)/2;
        int M = nums.length;

        int[][]dp = new int[M][bagSize+1];
        //首行-初始化
        if(nums[0] <= bagSize) {
            dp[0][nums[0]] = 1;
        }

        //首列-初始化
        int zeroNum = 0;
        for(int i=0; i<M; i++) {
            if(nums[i] == 0) {
                zeroNum++;
            }
            dp[i][0] = (int) Math.pow(2,zeroNum);
            // 有0的话,选或不选 2的x次方
            // 没有0,首列全部设为1??什么意思
        }

        for(int i=1; i<M; i++) {
            for(int j=1; j<bagSize+1; j++) {
                if(nums[i]>j) {
                    dp[i][j] = dp[i-1][j];
                } else {
                    dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i]];
                }
            }
        }

        return dp[M-1][bagSize];
    }
}

难点

(1)错误状况 return 0的情况

  //如果target的绝对值大于sum,那么是没有方案的
  if (Math.abs(target) > sum) return 0;
  //如果(target+sum)除以2的余数不为0,也是没有方案的
  if ((target + sum) % 2 == 1) return 0;

(2)递推公式 dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i]]的由来

在这里插入图片描述
(3)数组的初始化

  • 首行初始化:只初始化二维数组M*N里的一个格子,就是 j=nums[0] 时,也就是 只选 物品0,其余都不选,此时情况赋为1。
  if(nums[0] <= bagSize) {
     dp[0][nums[0]] = 1;
  }
  • 首列初始化:
    (1)数组元素有0的话,选或不选 2的x次方
    (2)数组元素没有0,首列全部设为1??什么意思
    dp[i][0] = 1,背包容量为0的时候,情况为1,或许可以理解为 这题本来就不是放背包,是求和??
    看到别人的解释:任何一个物品,只要选择不取的方案,就能凑成0容量的背包
	int zeroNum = 0;
	for(int i=0; i<M; i++) {
	    if(nums[i] == 0) {
	        zeroNum++;
	    }
	    dp[i][0] = (int) Math.pow(2,zeroNum);
	    // (1)数组元素有0的话,选或不选 2的x次方
	    // (2)数组元素没有0,首列全部设为1??什么意思
	    // dp[i][0] = 1,背包容量为
	}

出错点

① Math.pow(); 之前要写 (int) 强制类型转换

Line 32: error: incompatible types: possible lossy conversion from double to int
 dp[i][0] = Math.pow(2,zeroNum);

打印dp(为了方便理解)

(1)

  int[]nums = {1,1,1,1,1};
  int target = 3;
  • 初始化

在这里插入图片描述

  • 最终dp

在这里插入图片描述

(2)打印

  int[]nums = {0,1,0,1,1};
  int target = 1;
  • 初始化

在这里插入图片描述

  • 最终dp

在这里插入图片描述

474.一和零

我的思路

这道题放在代码随想录的0-1背包分类下,怎么抽象为背包问题?
重量满足两个维度 m n,同时尽可能是最大长度,三维背包?
int dp[strs.length][m+1][n+1]
dp[k][i][j]:0-k物品(strs[])中任选,拥有最大的子集长度(i表示0长度,j表示1长度)

1. 递推公式(假设和0-1推导方法相同)
(1)【不取】假设取物品k,但超过了容量(i,j)的限制,所以不放k
if(strs[k]0 1数量 > m n) 这里任意一个大于都不可以
dp[k][i][j] = dp[k-1][i][j]  
(2)【取】
A. 能放但是不放  dp[k][i][j] = dp[k-1][i][j]  
B. 能放且放了    dp[k][i][j] = dp[k-1][i-weight0[k]][j-weight1[k]]+value[k]
        weight0[k]是字符串k拥有的0的长度 ,weight1[k]是字符串k拥有的1的长度,value[k]是字符串k的长度
总和 dp[k][i][j] = Math.max(dp[k-1][i][j] , dp[k-1][i-weight0[k]][j-weight1[k]]+value[k])

2. 初始化dp数组
三维数组抽象为一个立方体
(1) dp[0][i][j] 相当于顶面,这一面表示物品0,放进容量为(i,j)的背包里,拥有的最大子集长度 
for(int t1=weight0[0]; t1<m+1; t1++ ) {
    for(int t2=weight1[0]; t2<n+1; t2++) {
        dp[0][i][j] = value[0]
    }
}

(2) dp[k][0][j] 首面,这一面表示物品k,放进容量为(0,j)的背包里,拥有的最大子集的长度,
for(int a=0; a<strs.length; a++ ) {
    for(int t=0; t<n+1; t++) {
        if(weight1[a] <= t) {
            dp[a][0][t] = value[a]
        }
    }
}

(3)dp[k][i][0]左面,这一面表示物品k,放进容量为(i,0)的背包里,拥有的最大子集的长度,
for(int a=0; a<strs.length; a++ ) {
    for(int t=0; t<m+1; t++) {
        if(weight0[a] <= t) {
            dp[a][t][0] = value[a]
        }
    }
}

3.遍历顺序
for(int a=0; a<strs.length; a++) {
    for(int t1=0; t1<m+1; t1++) {
        for(int t2=0; t2<n+1; t2++) {
            dp[a][t1][t2] = 
        }
    } 
}
  • 我的想法错误点:value表示的子集的多少,不是字符的长度。初始化想的很复杂

正确版本

class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        int len = strs.length;
        int[][][]dp = new int [len + 1][m + 1][n + 1];

        for(int i=1; i<len+1; i++) {
            int[]zeroOnes = getZeroOne(strs[i - 1]);
            int zero=zeroOnes[0], one=zeroOnes[1];
            for(int j=0; j<m+1; j++) {
                for(int k=0; k<n+1; k++) {
                    dp[i][j][k] = dp[i - 1][j][k];

                    if(zero <= j && one <= k) {
                        dp[i][j][k] = Math.max(dp[i - 1][j][k], dp[i - 1][j - zero][k - one]+1);
                    }
                }
            }
        }

        return dp[len][m][n];
    }

     public int[] getZeroOne(String s) {
        int[] num = new int[2];
        for(int i=0; i<s.length(); i++) {
            if(s.charAt(i) == '0') {
                num[0]++;
            } else {
                num[1]++;
            }
        }
        return num;
    }
}

错误点

这数组初始化在哪?
为什么for j=0 k=0从0开始?

java

  • 除以 2 :int target = sum >> 1;

  • 强制类型转换:int x = (int)Math.pow(a, b); 意思是 a的b次方
    public static double pow(double 基数,double 幂次)

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

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

相关文章

【Linux杂货铺】期末总结篇2:文件操作命令 | 目录操作命令

&#x1f308;个人主页&#xff1a;聆风吟_ &#x1f525;系列专栏&#xff1a;Linux实践室、网络奇遇记 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 第四章4.1 ⛳️Linux与windows的文件系统差别4.2 ⛳️目录相关的常用术语4.3 ⛳️Linux文件类型…

领航Linux UDP:构建高效网络新纪元

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ 文章目录 引言Udp和Tcp的异同相同点不同点总结 1.1、socket1.2、bind1.3、recvfrom1.4、sendto2.1、代码2.1、说明3.1、代码3.2、说明 引言 在前几篇博客中&#xff0c;我们学习了Linux网络编程中的一些概念。…

嵌入式人工智能(9-基于树莓派4B的PWM-LED呼吸灯)

1、PWM简介 (1)、什么是PWM 脉冲宽度调制(PWM)&#xff0c;是英文“Pulse Width Modulation”的缩写&#xff0c;简称脉宽调制&#xff0c;是在具有惯性的系统中利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术&#xff0c;广泛应用在从测量、通信到功率控制…

Linux部署禅道(无脑复制版)

目录 环境部署1、下载&#xff0c;解压2、启动3、设置开机自启 登录禅道登录数据库1、设置账号2、网页登录数据库 环境 Linux系统 Centos7 《Linux一键安装包安装禅道》视频链接&#xff1a; https://www.zentao.net/zentao-install/zentao-linux-install-80523.html 部署 …

2025考研~数据结构试卷

作者主页&#xff1a;知孤云出岫 数据结构试题 [TOC](数据结构试题)数据结构试卷一、选择题&#xff08;每题2分&#xff0c;共20分&#xff09;二、填空题&#xff08;每题3分&#xff0c;共15分&#xff09;三、简答题&#xff08;每题10分&#xff0c;共40分&#xff09;四…

c/c++ 打印调用栈

打印调用栈可以在程序出现死机的时候&#xff08;如出现 SIGABRT、SIGSEGV等一些信号错误&#xff09;是很有用的信息&#xff0c;有可能就不需要 core file 来协助排查问题了。通过 man backtrace 可以得到一个例子的源码&#xff1a; #define SIZE 100 static void backTrac…

《javeEE篇》--多线程(1)

进程 在讲线程之前我们先来简单了解一下进程 什么是进程 进程是操作系统对一个正在运行的程序的一种抽象&#xff0c;又或者说&#xff0c;可以把进程看作程序的一次运行过程(通俗的讲就是跑起来的程序)。 而且在操作系统内部&#xff0c;进程是资源分配的基本单位 PCB P…

学生基本信息界面(MFC)

本文将引用MFC常用控件&#xff0c;写一个学生基本信息界面&#xff0c;最后将统计结果显示在提示框中&#xff0c;运行效果如下&#xff1a; 1.新建基于对话框的MFC项目&#xff0c;布局对话框&#xff0c;修改相应控件ID并绑定变量 注意:第一个单选控件的group属性 3.在构造…

《算法笔记》总结No.7——二分(多例题详解版)

一.二分查找 目前有一个有序数列&#xff0c;举个例子&#xff0c;假设是1~1000&#xff0c;让我们去查找931这个数字&#xff0c;浅显且暴力的做法就是直接从头到尾遍历一遍&#xff0c;直到找到931为止。当n非常大&#xff0c;比如达到100w时&#xff0c;这是一个非常大的量级…

获取欧洲时报中国板块前新闻数据(多线程版)

这里写目录标题 一.数据获取流程二.获取主页面数据并提取出文章url三.获取文章详情页的数据并提取整体代码展示 一.数据获取流程 我们首先通过抓包就能够找到我们所需数据的api 这里一共有五个参数其中只有第一个和第五个参数是变化的第一个参数就是第几页第五个是一个由时…

论文翻译 | Successive Prompting for Decomposing Complex Questions 分解复杂问题的连续提示

摘要 回答需要做出潜在决策的复杂问题是一项具有挑战性的任务&#xff0c;尤其是在监督有限的情况下。 最近的研究利用大型语言模型&#xff08;LMs&#xff09;的能力&#xff0c;在少量样本设置中通过展示如何在单次处理复杂问题的同时输出中间推理过程&#xff0c;来执行复杂…

【自学安全防御】二、防火墙NAT智能选路综合实验

任务要求&#xff1a; &#xff08;衔接上一个实验所以从第七点开始&#xff0c;但与上一个实验关系不大&#xff09; 7&#xff0c;办公区设备可以通过电信链路和移动链路上网(多对多的NAT&#xff0c;并且需要保留一个公网IP不能用来转换) 8&#xff0c;分公司设备可以通过总…

Jdk8 Idea Maven Received fatal alert: protocol_version

问题描述 使用idea开发工具&#xff0c;maven加载项目依赖时&#xff0c;出现错误&#xff1a; Could not transfer artfact xxxxxxx from/to maven-dep-repos https://XXXXXXX: Received fatal alert: protocol_version初步思路 用关键字protocol_version 去检索&#xff0…

Schematics,一个牛逼的python库用于数据验证和转换的库

目录 什么是Schematics&#xff1f; 为什么使用Schematics&#xff1f; 安装Schematics 定义模式 验证数据 自定义验证 转换数据 结语 什么是Schematics&#xff1f; 在Python的世界中&#xff0c;Schematics是一个用于数据验证和转换的库。它通过定义数据结构的模式(…

30秒学会UML-功能类图

目录 1、类图本体 三部分 修饰符 2、类与类直接关系 泛化关系 实现关系 简单关联关系 依赖关系 组合关系 聚合关系 1、类图本体 三部分 第一层&#xff1a;类名第二层&#xff1a;成员变量&#xff08;类的属性&#xff09;第三层&#xff1a;函数方法&#xff08;类…

PX4 运行 make px4_sitl_default gazebo 报错

报错原因&#xff1a;最开始我把依赖一直都是在base环境下安装的&#xff0c;没有conda deactivate&#xff0c;而pip install的东西应该装在系统环境&#xff0c;不能装在base环境下&#xff0c;sudo apt 是装在系统环境的 1.检查ros 用鱼香ros安装 wget http://fishros.…

SSL证书续费

讲解下域名证书如何续费&#xff08;以阿里云为例&#xff09; ‍ 提醒 一般云服务器厂商&#xff0c;都会提前和你一个月左右通知&#xff08;邮件、短信等&#xff09;&#xff0c;例如&#xff1a; 尊敬的 xxx&#xff1a;您域名 www.peterjxl.com 使用的 SSL 证书 xxxxx…

Linux编程(通信协议---udp)

UDP&#xff08;用户数据报协议&#xff09;是一种无连接的网络协议&#xff0c;主要用于快速传输数据。以下是UDP协议的一些主要特点&#xff1a; 1. **无连接**&#xff1a;UDP是无连接的协议&#xff0c;这意味着在数据传输之前不需要建立连接。每个UDP数据包都是独立的&am…

数据库操作太复杂?Python Shelve模块让你轻松存储,一键搞定!

目录 1、基本操作入门 &#x1f4da; 1.1 安装Shelve模块 1.2 创建与打开Shelve文件 2、存储与读取数据 &#x1f510; 2.1 写入键值对 2.2 读取存储的数据 3、高级功能探索 &#x1f9ed; 3.1 使用Shelve迭代键和值 3.2 键的管理&#xff1a;添加、删除与更新 4、异…

minishell

今天完成了minishell的制作 项目需求&#xff1a; 1. 获取终端用户输入的命令&#xff0c;并输出相应的执行结果。 touch cp mv ls ls -a ls -l mkdir rmdir pwd cd ln ln -s exit ---------…