【力扣刷题 | 第二十四天】

目录

前言:

[1049. 最后一块石头的重量 II - 力扣(LeetCode)](#1049. 最后一块石头的重量 II - 力扣(LeetCode))

[494. 目标和 - 力扣(LeetCode)](#494. 目标和 - 力扣(LeetCode))

总结:


前言:

今天我们依然暴打动态规划

1049. 最后一块石头的重量 II - 力扣(LeetCode)

有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。

每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:

如果 x == y,那么两块石头都会被完全粉碎;

如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。

最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0。

这个问题其实很好想:我们如何把石头尽可能的撞碎呢?

很简单,我们在大体上把这些石头分成重量相似的两堆,因为如果两堆石头的重量相似,那么相撞后就一定会得到最小的值。

因此从动态分类的角度来讲,我们可以把这道题理解为是 一个重量为(sum/2)的背包,我们尽可能的装背包,之后装进背包的是一堆石头,未装进背包的是一堆石头,那么这两堆石头相减,就可以得到最小的石头重量。

那么我们就又把这道题转化为了 装与不装的01背包问题

其实这道题和我们前天做的分割等和子集是基本相同的,感兴趣的朋友可以去看一下那道题

那么既然是动态规划问题,我们就继续进行动态规划五部曲:

1.确定dp数组及其含义:dp[j] 是背包含量为j的时候所背的最大价值(在这里我们重量就是价值,价值就是重量)

2.递推公式 dp[j] = max(dp[j] , dp[ j - stones[i] ]+stones[i])

里层的减是减去容量,外面的加是加上价值,只不过是因为我们的重量和价值是一样的

3.确定初始化:全部初始化为0即可

cpp 复制代码
class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
            int sum=0;
            vector<int>dp(1501,0);
        for(int a : stones)
        {
            sum=sum+a;
        }

        int target = sum/2;

        for(int i=0;i<stones.size();i++)
        {
            for(int j= target;j>=stones[i];j--)
            {
                 dp[j] = max(dp[j] , dp[ j - stones[i] ]+stones[i]);
            }
        }
        return sum-dp[target]-dp[target];
    }
};

在这里我们要特别对末尾进行说明,为什么是 return sum-dp[target]-dp[target];

其实很好理解,因为这里的dp数组是一堆石头,而我们用sum-dp数组就得到了另外一堆石头的重量,再减一次dp数组,实际就是模拟两堆石头进行碰撞。

494. 目标和 - 力扣(LeetCode)

给你一个非负整数数组 nums 和一个整数 target 。

向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 :

例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。

返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

这道题其实我们可以这样理解:

一共有两个集合,两个集合分别装数字,设置目标值target,询问有几种可能使得两个集合相减的数字等于target

再优化一下:

我们可以把target也作为一个数字加到这两个集合的待选序列,询问有几种可能 使得两个集合的值相等

再转化一下思维:

,而target只有一个,也就是只能永远出现在一侧,另外一侧始终没有target,那么我们始终求没有target的那一侧不就好了嘛,换句话说,不就是在给定集合里面寻找有多少个集合的值等于(sum+target)/2/

那么我们不就又把这道题转化为了背包问题嘛?只不过以前我们总是在求是否有一种方法能把背包装满,现在求的是有多少种方法把背包装满。

因此我们按照动态规划五部曲走:

1.确定dp数组的含义:dp[j] 表示填满容量为j的背包最多有dp[j]种方法,例如dp[2]是指填满容量为2的背包最多有dp[2]种方法。

2.确定递推公式:dp[j]+=dp[j-nums[i]];

cpp 复制代码
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum=0;
        for(int a : nums)
        {
            sum=sum+a;
        }

        if((target + sum)%2==1) return 0;
        if(abs(target)>sum) return 0;
        else
        {
              int a = (target + sum) / 2;
              vector<int> dp(a + 1, 0);
             dp[0] = 1;
            for (int i = 0; i < nums.size(); i++) 
            {
               for (int j = a; j >= nums[i]; j--) 
               {
                dp[j] += dp[j - nums[i]];
                }
            }
        return dp[a];
        }
    }
};

总结:

动态规划想清楚之后直接爆杀。

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

相关推荐
BUG收容所所长25 分钟前
二分查找的「左右为难」:如何优雅地找到数组中元素的首尾位置
前端·javascript·算法
itsuifengerxing1 小时前
python 自定义无符号右移
算法
猎板PCB厚铜专家大族2 小时前
高频 PCB 技术发展趋势与应用解析
人工智能·算法·设计规范
dying_man2 小时前
LeetCode--24.两两交换链表中的结点
算法·leetcode
yours_Gabriel2 小时前
【力扣】2434.使用机器人打印字典序最小的字符串
算法·leetcode·贪心算法
草莓熊Lotso2 小时前
【数据结构初阶】--算法复杂度的深度解析
c语言·开发语言·数据结构·经验分享·笔记·其他·算法
KyollBM3 小时前
【CF】Day75——CF (Div. 2) B (数学 + 贪心) + CF 882 (Div. 2) C (01Trie | 区间最大异或和)
c语言·c++·算法
CV点灯大师3 小时前
C++算法训练营 Day10 栈与队列(1)
c++·redis·算法
GGBondlctrl3 小时前
【leetcode】递归,回溯思想 + 巧妙解法-解决“N皇后”,以及“解数独”题目
算法·leetcode·n皇后·有效的数独·解数独·映射思想·数学思想
武子康3 小时前
大数据-276 Spark MLib - 基础介绍 机器学习算法 Bagging和Boosting区别 GBDT梯度提升树
大数据·人工智能·算法·机器学习·语言模型·spark-ml·boosting