代码随想录算法训练营第四十三天|1049. 最后一块石头的重量 II、494. 目标和、474.一和零

代码随想录算法训练营第四十三天|1049. 最后一块石头的重量 II、494. 目标和、474.一和零

最后一块石头的重量

1049. 最后一块石头的重量
文章讲解:https://programmercarl.com/1049.%E6%9C%80%E5%90%8E%E4%B8%80%E5%9D%97%E7%9F%B3%E5%A4%B4%E7%9A%84%E9%87%8D%E9%87%8FII.html
题目链接:https://leetcode.cn/problems/last-stone-weight-ii/
视频讲解:https://www.bilibili.com/video/BV14M411C7oV/

自己看到题目的第一想法

动态规划五步骤:

  • dp[j]含义:从下标为[0-j]的物品里任意取,放进背包后,价值总和最大是多少。
  • 确定递推公式:dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
  • dp数组初始化:dp[0] = 0;飞、非0下标初始化为0。
  • 遍历顺序:一维数组,从后往前遍历

看完代码随想录之后的想法

先列出物品的重量、价值表格。

重量 价值
物品0 1 15
物品1 3 20
物品2 4 30

得到dp[i][j]的定义

动态规划五步骤:

  • dp[i][j]的定义:dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。

  • 递推公式:不放物品i的时候,最大价值为dp[i-1][j](其实就是当物品i的重量大于背包j的重量时,物品i无法放进背包中,所以背包内的价值依然和前面相同。因此在循环时会有背包大小的判断);放物品i的时候最大价值为dp[i-1][j - wight[i]] + value[i](当i放进去时,那么这时候整个物品集就被分成两部分,1到i-1和第i个,而这是i是确定要放进去的,那么就把j空间里的wight[i]给占据了,只剩下j-wight[i]的空间给前面i-1,那么只要这时候前面i-1在j-wi空间里构造出最大价值,即dp[i-1][j - wight[i]],再加上此时放入的i的价值vi,就是dpij了);所以递推公式dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j - wight[i]] + value[i]);见下面表格,dp[i][j]由其上一个节点以及左上某一节点得到。在这里插入图片描述

  • 初始化值:画出背包重量和物品的二维数组图。见下图可以得出dp[0][j]这一行的值为15。dp[i][0]这一列的初始值为0。在这里插入图片描述

  • 遍历顺序

    • for() ----- 物品
      • for()----背包
        这里进行遍历的时候需要增加一个当前重量放不下的判断。

自己实现过程中遇到哪些困难

将2个数组入参改为二维数组,然后处理逻辑。
做动态规划的题目,最好的过程就是自己在纸上举一个例子把对应的dp数组的数值推导一下,然后在动手写代码!

目标和

494. 目标和
文章讲解:https://programmercarl.com/%E8%83%8C%E5%8C%85%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%8001%E8%83%8C%E5%8C%85-2.html
题目链接:https://leetcode.cn/problems/target-sum/
视频讲解:https://www.bilibili.com/video/BV1o8411j73x

自己看到题目的第一想法

想不出来。

看完代码随想录之后的想法

这里看代码随想录没理解,后面看了leetcode官方题解

代码随想录

输入的所有数字可以分为两组,一组是正数组,一组是负数组。
两组的数字求和后为sum。两组的数字求差后为target。
left + right = sum。
left - right = target。
target是固定的,sum是固定的,left就可以求出来。left = (sum + target) / 2。
这里的推导可以转换为,装满容量为left的背包有几种方法。

  • 当left = (sum + target) / 2计算过程中出现未除尽情况,此时无解。(因为数组中的元素都为非负整数,因此left也必须是非负整数
  • 同理如果target的绝对值大于sum,也是没有方案的。
leetCode官方题解

使用二维数组,整体和代码随想录前面差不多,记数组的元素和为 sum,添加 - 号的元素之和为 neg,则其余添加 + 的元素之和为 sum−neg。求得neg = (sum-target)/2。
由于数组nums中的元素都是非负整数,neg也必须是非负整数,因此除不尽的情况下直接返回0。
动态规划五步骤:

  • 定义二维数组dp,dp[i][j]表示在数组nums的前i个数中选取元素,使得这些元素之和等于j的方案数。假设数组nums的长度为n,则最终答案为dp[n][neg]。

  • 确定推导公式

    • 代码随想录算法训练营第四十三天|1049. 最后一块石头的重量 II、494. 目标和、474.一和零_第1张图片
  • 确定初始化值,当没有任何元素可选时,元素和只能是0。对应的方案数是1,dp[0][0] = 1;其他情况下dp[0][j] = 0。

自己实现过程中遇到哪些困难

一和零

 474.一和零  
文章讲解:https://programmercarl.com/0474.%E4%B8%80%E5%92%8C%E9%9B%B6.html
题目链接:https://leetcode.cn/problems/ones-and-zeroes/
视频讲解:https://www.bilibili.com/video/BV1rW4y1x7ZQ

自己看到题目的第一想法

看完代码随想录之后的想法

将题目想成一个容器,最多m个0,n个1。
动态规划五步骤:

  • dp数组定义,定义一个二维dp数组(一维数组没法表现m、n以及最多多少个物品)。装满i个0,j个1,最多背多少个物品。最大背dp[i][j]个物品 -> dp[m][n]。dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]。
  • 确定递推公式:该题每个物品的重量为x个0,y个1。总体是即m个0,n个1.每次放进一个字符时,该字符中的0的个数即为x,1的个数即为y。则当前的物品数量为dp[i-x][j-y]+1(前面dp[i-x][j-y]表示没放x,y时的数量。后面+1表示放了x,y这个字符的,价值(物品数量)为1)
    • dp[i][j] = max(dp[i-x][j-y] + 1,dp[i][j])。
  • 确定初始化值:dp[0][0]=0,因为背包容量(这里表示m,n为0)为0了,所装的最大个数自然为0;其他非0的初始化成0,不能初始为integer最大值,因为要取max。dp[i][j] 可以由前一个strs里的字符串推导出来,strs里的字符串有zeroNum个0,oneNum个1。dp[i][j] 就可以是 dp[i - zeroNum][j - oneNum] + 1。
  • 确定遍历顺序:先遍历物品,再遍历背包。遍历背包时倒序遍历。物品是字符串,背包是X和Y。i从m开始,大于等于x再–;j从n开始,大于等于y再–。(迭代格式中,某个单元的值由前面的单元得来,所以你先更新前面的时候,后面的值本来是应该上一时刻的值,但是顺序计算时,旧的左值被新值覆盖了。)

自己实现过程中遇到哪些困难

基本上算是复制了代码,其实看完也没看懂。这一节课卡的时间太长了,快速过了先。后面二刷

 public int findMaxForm(String[] strs, int m, int n) {
     //dp[i][j]表示i个0和j个1时的最大子集
     int[][] dp = new int[m + 1][n + 1];
     int oneNum, zeroNum;
     for(String str : strs){ // 遍历背包
         oneNum = 0;
         zeroNum = 0;
         for (char ch : str.toCharArray()) {
             if (ch == '0') {
                 zeroNum++;
             } else {
                 oneNum++;
             }
         }
         for(int i = m; i >= zeroNum; i--){
             for(int j = n; j >= oneNum; j--){
                 dp[i][j] = Math.max(dp[i][j],dp[i - zeroNum][j - oneNum] + 1);
             }
         }
     }
     return dp[m][n];
 }

今日收获&学习时长

这节课卡了很久,后续背包类型题目全部都需要二刷,基本没有想出来的。

你可能感兴趣的:(算法)