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

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 幂次)

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

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

相关文章

【学习笔记】虚幻SkeletalMesh学习(一)基础介绍

文章目录 零、前言一、资源介绍1.1 骨架资源1.2 骨架网格体资源 二、UE4中的定义2.1 骨骼数据2.2 模型网格数据 三、渲染3.1 RenderData的初始化3.2 渲染对象的创建3.3 渲染对象的更新3.3.1 游戏线程的更新&#xff08;*FSkeletalMeshObjectGPUSkin::Update*&#xff09;3.3.2 …

java 发送企业域名邮箱消息

目录 通过域名注册邮箱准备添加用户登录 通过java发送企业邮件pom.xml发送代码 企业为了推广本公司的知名度&#xff0c;系统注册邮箱时&#xff0c;发送验证码得邮箱&#xff0c;需要以域名为后缀 通过域名注册邮箱 首选拥有一个企业域名&#xff0c;本文默认大家都有域名 准…

浏览器缓存:强缓存与协商缓存实现原理有哪些?

1、强缓存&#xff1a;设置缓存时间的&#xff0c;那么在这个时间内浏览器向服务器发送请求更新数据&#xff0c;但是服务器会让其从缓存中获取数据。 可参考&#xff1a;彻底弄懂强缓存与协商缓存 - 简书 2、协商缓存每次都会向浏览器询问&#xff0c;那么是怎么询问的呢&…

家用美容仪维修图片记录

家用美容仪维修过程记录&#xff0c;宙斯&#xff0c;上图

JavaEE初阶-网络原理2

文章目录 前言一、TCP报头结构二、TCP的十个核心机制2.1 确认应答2.2 超时重传2.3 连接管理2.3.1 建立连接&#xff1a;三次握手2.3.2 断开连接&#xff1a;四次挥手. 2.4 滑动窗口2.5 流量控制2.6 拥塞控制2.7 延时应答2.8 捎带应答2.9 面向字节流2.10 异常情况2.11 补充 前言…

Java(二十)---双向链表

文章目录 前言1.为什么学习双向链表2.双向链表(LinkedList)的模拟实现2.1. 准备工作2.2.功能的实现2.2.1.显示链表(display) 和 是否包含某种元素(contains) 以及 获取链表节点个数(size())2.2.2.头插法(addFirst)&#xff0c;尾插法(addLast)&#xff0c;以及在指定位置进行插…

鸿蒙语言基础类库:【@system.brightness (屏幕亮度)】

屏幕亮度 说明&#xff1a; 从API Version 7 开始&#xff0c;该接口不再维护&#xff0c;推荐使用新接口[ohos.brightness]。本模块首批接口从API version 3开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import brightness from sy…

基于SpringBoot+VueJS+微信小程序技术的图书森林共享小程序设计与实现:7000字论文+源代码参考

博主介绍&#xff1a;硕士研究生&#xff0c;专注于信息化技术领域开发与管理&#xff0c;会使用java、标准c/c等开发语言&#xff0c;以及毕业项目实战✌ 从事基于java BS架构、CS架构、c/c 编程工作近16年&#xff0c;拥有近12年的管理工作经验&#xff0c;拥有较丰富的技术架…

ZBrush入门使用介绍——2、GoZ使用

大家好&#xff0c;我是阿赵。   这里介绍一下ZBrush的GoZ功能。 一、 GoZ工具的作用 GoZ工具&#xff0c;是一个可以把ZBrush里面的模型发送到别的软件&#xff0c;还有可以从别的软件把模型发送到ZBrush的工具。   暂时&#xff0c;GoZ支持Cinema4D、3D Studio Max、May…

Go Web开发框架之Gin

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

代码规范性

代码规范性 命名规范代码格式注释代码结构异常处理代码复用代码审查空格的用法代码示例 命名规范 ​ 1、变量和函数名&#xff1a;使用驼峰命名法&#xff08;camelCase&#xff09;&#xff0c;如userName、getUserInfo。 ​ 2、常量&#xff1a;使用全大写字母&#xff0c;…

CompletableFuture异步编排

1.创建异步对象 CompletableFuture提供了四个静态方法来创建一个异步操作 public static ExecutorService executor Executors.newFixedThreadPool(10);public static void main(String[] args) throws ExecutionException, InterruptedException {System.out.println("…

mwwz库支持可视化每个特征点的匹配质量

支持获取每个特征点的匹配分数&#xff0c;同时支持擦除特征点。

数据库第6次作业

内容 1、创建视图v_emp_dept_id_1&#xff0c;查询销售部门的员工姓名和家庭住址 2、创建视图v_emp_dept&#xff0c;查询销售部门员工姓名和家庭住址及部门名称。 3、创建视图v_dept_emp_count(dept_name,emp_count,avg_salay)&#xff0c;统计每个部门人数并计算平均工资。 …

【Datawhale AI夏令营】电力需求预测挑战赛 Task02

task02 Task2 版本教程将使用机器学习模型解决本次问题&#xff0c;模型使用简单&#xff0c;数据不需要过多预处理&#xff1b; 使用机器学习方法一般主要需要从 获取数据&增强、特征提取和模型 三个方面下手。 使用机器学习方法有哪几个步骤&#xff1f; 一般的使用机器…

摄像头 RN6752v1 视频采集卡

摄像头 AHD倒车摄像头比较好&#xff0c;AHD英文全名Analog High Definition&#xff0c;即模拟高清&#xff0c;拥有比较好的分辨率与画面质感。 RN6752v1 GQW AKKY2 usb 采集卡 FHD&#xff08;1080p&#xff09;、HD&#xff08;720p&#xff09;和D1&#xff08;480i&am…

局域网内放开端口

欢迎使用Markdown编辑器 点击完成后&#xff0c;其他内网机器就可以访问了。

ICT产业是什么?具体是干什么

前言&#xff1a; ICT产业&#xff0c;即信息与通信技术产业&#xff08;Information and Communication Technology&#xff09;&#xff0c;是一个涵盖了广泛技术和服务的综合产业。它主要包括计算机硬件、软件、网络和电信设备等领域。 ICT是由信息通信和技术的英文单词首…

Linux 内核模块加载知多少

文章目录 目录 1. 内核模块 内核模块的作用 2. 内核模块的加载 2.1 内核模块的加载过程 2.2 内核模块加载方式 使用 insmod 加载模块 使用 modprobe 加载模块 2.3 内核模块加载顺序 3. 常用的相关命令 4. 总结 工作还在继续&#xff0c;学习还在继续&#xff0c;学习…

RK3568笔记三十七:按键驱动实验(设备树)

若该文为原创文章&#xff0c;转载请注明原文出处。 一、编程思路 程序编写的主要内容为添加 key 的设备树节点、在驱动程序中使用 of 函数获取设备节点中的属性&#xff0c;编写测试应用程序。 • 首先向设备树添加 key 设备节点。 • 其次编写平台设备驱动框架&#xff0c;…