首页 > 其他分享 >day45 1049.最后一块石头的重量II 494.目标和 474.一和零

day45 1049.最后一块石头的重量II 494.目标和 474.一和零

时间:2024-06-02 13:03:14浏览次数:22  
标签:背包 target nums int sum II 474 494 dp

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

本题其实就是尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小,这样就化解成01背包问题了

本题物品的重量为stones[i],物品的价值也为stones[i]。

对应着01背包里的物品重量weight[i]和 物品价值value[i]。

思路:

动规五部曲

1.确定dp数组以及下标的含义

dp[j]表示容量(这里说容量更形象,其实就是重量)为j的背包,最多可以背最大重量为dp[j]

2.确定递推公式

dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);

3.dp数组如何初始化

把石头遍历一遍,计算出石头总重量 然后除2,得到dp数组的大小。

因为重量都不会是负数,所以dp[j]都初始化为0就可以了,这样在递归公式dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);中dp[j]才不会初始值所覆盖。

4.确定遍历顺序

如果使用一维dp数组,物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历!

5.举例推导dp数组

补充:

在计算target的时候,target = sum / 2 因为是向下取整,所以sum - dp[target] 一定是大于等于dp[target]的

那么相撞之后剩下的最小石头重量就是 (sum - dp[target]) - dp[target]。

   class Solution {
        public int lastStoneWeightII(int[] stones) {
            if (stones == null || stones.length == 0) return 0;
            int n = stones.length;
            int sum = 0;
            for (int num : stones) {
                sum += num;
            }
            int target = sum >> 1;
            int[] dp = new int[target + 1];
            for (int i = 0; i < n; i++) {
                for (int j = target; j >= stones[i]; j--) {
                    dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]);
                }
            }
            return sum - 2 * dp[target];
        }
    }

494.目标和

本题要如何使表达式结果为target,

既然为target,那么就一定有 left组合 - right组合 = target。

left + right = sum,而sum是固定的。right = sum - left

公式来了, left - (sum - left) = target 推导出 left = (target + sum)/2 。

此时问题就转化为,装满容量为x的背包,有几种方法

再回归到01背包问题,为什么是01背包呢?

因为每个物品(题目中的1)只用一次!

这次和之前遇到的背包问题不一样了,之前都是求容量为j的背包,最多能装多少。

本题则是装满有几种方法。其实这就是一个组合问题了。

思路:

动规五部曲

1.确定dp数组以及下标的含义

dp[j] 表示:填满j(包括j)这么大容积的包,有dp[j]种方法

2.确定递推公式

只要搞到nums[i],凑成dp[j]就有dp[j - nums[i]] 种方法。

例如:dp[j],j 为5,

       已经有一个0(nums[i]) 的话,有 dp[5]种方法 凑成 容量为5的背包。

  • 已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 容量为5的背包。
  • 已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 容量为5的背包。
  • 已经有一个3(nums[i]) 的话,有 dp[2]中方法 凑成 容量为5的背包
  • 已经有一个4(nums[i]) 的话,有 dp[1]中方法 凑成 容量为5的背包
  • 已经有一个5 (nums[i])的话,有 dp[0]中方法 凑成 容量为5的背包

那么凑整dp[5]有多少方法呢,也就是把 所有的 dp[j - nums[i]] 累加起来

3.dp数组如何初始化

从递推公式可以看出,在初始化的时候dp[0] 一定要初始化为1,因为dp[0]是在公式中一切递推结果的起源,如果dp[0]是0的话,递推结果将都是0。

eg:

如果数组[0] ,target = 0,那么 bagSize = (target + sum) / 2 = 0。 dp[0]也应该是1, 也就是说给数组里的元素 0 前面无论放加法还是减法,都是 1 种方法。

所以本题我们应该初始化 dp[0] 为 1。

那 如果是 数组[0,0,0,0,0] target = 0 呢。

其实 此时最终的dp[0] = 32,也就是这五个零 子集的所有组合情况,但此dp[0]非彼dp[0],dp[0]能算出32,其基础是因为dp[0] = 1 累加起来的。

dp[j]其他下标对应的数值也应该初始化为0,从递推公式也可以看出,dp[j]要保证是0的初始值,才能正确的由dp[j - nums[i]]推导出来。

4.确定遍历顺序

一维dp的遍历,nums(物品)放在外循环,target(背包)在内循环,且内循环倒序。

5.举例推导dp数组

输入:nums: [1, 1, 1, 1, 1], S: 3

bagSize = (S + sum) / 2 = (3 + 5) / 2 = 4

如果S=2 则求不出这样的集合即(S + sum) / 2不能整除

代码

 class Solution {
        public int findTargetSumWays(int[] nums, int target) {
            int n = nums.length;
            int sum = 0;
            for (int num : nums) {
                sum += num;
            }
            if (Math.abs(target) > sum) return 0;
            if ((target + sum) % 2 == 1) return 0;   //不能整除 凑不成target 没有组合

            int bagSize = (target + sum) / 2;
            int[] dp = new int[bagSize + 1];  
            Arrays.fill(dp, 0);
            dp[0] = 1;
            for (int i = 0; i < n; i++) {
                for (int j = bagSize; j >= nums[i]; j--) {
                    dp[j] += dp[j - nums[i]];
                }
            }
            return dp[bagSize];
        }
    }

474.一和零

本题并不是多重背包,多重背包是每个物品,数量不同的情况。

本题中strs 数组里的元素就是物品,每个物品都是一个!

而m 和 n相当于是一个背包,两个维度的背包

思路:

动规五部曲

1.确定dp数组(dp table)以及下标的含义

dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]

2.确定递推公式

dp[i][j] 可以由前一个strs里的字符串推导出来,strs里的字符串有zeroNum个0,oneNum个1。

dp[i][j] 就可以是 dp[i - zeroNum][j - oneNum] + 1。

然后我们在遍历的过程中,取dp[i][j]的最大值。

所以递推公式:dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);

3.dp数组如何初始化

当背包的容量为0时是放不下物品的 故dp[0][0]=0

01背包的dp数组初始化为0就可以。

因为物品价值不会是负数,初始为0,保证递推的时候dp[i][j]不会被初始值覆盖。

4.确定遍历顺序

外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历!

那个遍历背包容量的两层for循环先后循序有没有什么讲究?

没讲究,都是物品重量的一个维度,先遍历哪个都行!

5.举例推导dp数组

以输入:["10","0001","111001","1","0"],m = 3,n = 3为例

代码

   class Solution {
        public int findMaxForm(String[] strs, int m, int n) {
            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];
        }
    }

标签:背包,target,nums,int,sum,II,474,494,dp
From: https://blog.csdn.net/m0_68259754/article/details/139339732

相关文章

  • day46 完全背包理论基础 518. 零钱兑换 II 377. 组合总和 Ⅳ
    完全背包理论基础有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i]。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。01背包内嵌的循环是从大到小遍历,为了保证每个物品仅被添加一次。而完全......
  • 代码随想录算法训练营第第25天 | 216.组合总和III 、17.电话号码的字母组合
    今天的题比较简单,重点是在于剪枝216.组合总和III如果把组合问题理解了,本题就容易一些了。题目链接/文章讲解:https://programmercarl.com/0216.组合总和III.html视频讲解:https://www.bilibili.com/video/BV1wg411873x/***@param{number}k*@param{number}n*@retu......
  • (11.1)iic串口读写EEPROM实验:EEPROM介绍
    一、EEPROM简介EEPROM(ElectricallyErasableProgrammableReadOnlyMemory),带电可擦除可编程只读存储器,是一种掉电后数据不丢失的非易失性存储器,用户可以通过高于普通电压的作用来擦除和编程(重写)非易失性存储器主要包括:EEPROM:以字节为单位改写;结构复杂,成本高;存储......
  • 深入解析力扣170题:两数之和 III - 数据结构设计(哈希表与双指针法详解及模拟面试问答)
    在本篇文章中,我们将详细解读力扣第170题“两数之和III-数据结构设计”。通过学习本篇文章,读者将掌握如何设计一个数据结构来支持两种操作,并了解相关的复杂度分析和模拟面试问答。每种方法都将配以详细的解释和ASCII图解,以便于理解。问题描述力扣第170题“两数之和III......
  • 代码随想录算法训练营第四十五天 | 1049. 最后一块石头的重量 II、494. 目标和、474.
    1049.最后一块石头的重量II视频讲解:动态规划之背包问题,这个背包最多能装多少?LeetCode:1049.最后一块石头的重量II_哔哩哔哩_bilibili代码随想录解题思路直接将这一些石头,分为两堆,让他们尽可能相似,然后再相撞,就是最小值1.dp[j]背包容量为j所背的最大价值2.dp[......
  • C# 检测并重启windows服务,IIS应用池
      usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;usingSystem.Data;usingSystem.Diagnostics;usingSystem.Linq;usingSystem.ServiceProcess;usingSystem.Text;usinglog4net;usingSystem.Timers;usingSystem.Configurati......
  • Leedcode-反转字符串 II
    自己写的:classSolution:defreverseStr(self,s:str,k:int)->str:#初始化两个空列表:s_li用于存储切分后的字符串片段,res用于存储处理后的片段s_li=[]res=[]#遍历字符串,步长为2*k,切分成每2*k个字符一组的片段并存储在s_li......
  • Windows Server系统中如何通过IIS创建Web站点
    概述本文主要介绍在WindowsServer系统中,如何通过IIS创建Web站点。详细信息根据您的操作系统版本,选择对应的操作步骤。由于WindowsServer2008和2012的步骤一致,此处以WindowsServer2012版本为例。提示:通过IIS创建站点前需要确认您的Windows实例已经安装IIS服务,如未安装IIS......
  • P110 III
     1   Il.ParaphraseExplainthefollowingsentencesinyourownwords,bringingoutanyimpliedmeanings1....withafacethatseemedtotallyunfamiliarwithlaughter...(Para.2)2SometimesoldJules,orhissonLazarus,wouldgetmixedupinaSaturda......
  • 力扣-474. 一和零
    1.题目题目地址(474.一和零-力扣(LeetCode))https://leetcode.cn/problems/ones-and-zeroes/题目描述给你一个二进制字符串数组strs和两个整数m和n。请你找出并返回strs的最大子集的长度,该子集中最多有m个0和n个1。如果x的所有元素也是y的元素,集合......