LeetCode刷题day55

简介: LeetCode刷题day55

494. 目标和

题目描述

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


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


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

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


示例 1:

输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3

示例 2:

输入:nums = [1], target = 1
输出:1


思路分析


表达式和为target,可以看成left-right=target.即 left-(sum-left) = target=>left = (sum+target)/2


所以题目就可以转化为在集合nums中找出和为left的组合。


如何转化为0-1背包问题呢。


假设加法的总和为x,那么减法对应的总和就是sum - x。


所以我们要求的是 x - (sum - x) = S ==> x = (S + sum) / 2


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


但是此时需要注意一点:如果(S+sum) % 2 == 1,则是无解的,说明无法找到满足的.因为背包容量都是整数.


另外如果abs(target) > sum,则也无解.因为所有的数同号也无法达到target.

if(abs(target) > sum) {
    return 0;//没有方案 
}
if((sum+target) % 2==1) {//没有方案 
    return 0;
}

动规三部曲


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

dp[j]:填满j这么大容积的背包,有dp[j]种方法


这个题也可以使用二维dp数组,dp[i] [j] = 使用下标为[0,i]的nums[i]能够凑满j这么大容积的背包需要dp[i] [j]种方法.


确定递推公式

dp[j] = dp[j] + dp[j-nums[i]] ==>dp[j] 等于不使用当前物体(数)的情况+使用当前物体(数)的情况


dp数组初始化

dp[0] = 1,装满容量为0的背包,只有一种方法,就是装0件物品.


确定遍历顺序

根据01背包问题,一维dp数组遍历,物品放在外层循环,背包容量放在内层倒序循环.


5.举例推导dp数组


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


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


dp数组状态变化如下:


3e6445e49619117b4d75d6fa729c0ea2.png


参考代码


#include<bits/stdc++.h>
using namespace std;
//打印dp数组
print(vector<int>& dp){
  for(int i = 0;i < dp.size(); i++){
    printf("%3d",dp[i]);
  }
  cout<<endl;
}
int findTargetSumWays(vector<int>& nums, int target) {
  int sum = 0;
  for(int i = 0; i < nums.size(); i++) {
    sum += nums[i];
  }
  if(abs(target) > sum) {
    return 0;//没有方案 
  }
  //left - (sum - left) = target==>left = (sum+target) / 2
  if((sum+target) % 2==1) {//没有方案 
    return 0;
  }
  int bageSize = (sum+target) / 2;
  vector<int> dp(bageSize+1,0);
  dp[0] = 1;
  for(int i = 0; i < nums.size(); i++) {
    for(int j =bageSize;  j>=nums[i]; j--) {
      dp[j] = dp[j]+dp[j-nums[i]];//当前物体放的情况d[j-nums[i]]+当前数字不放的情况d[j]便是总情况 
    }
    //cout<<"dp["<<i<<"]";
    //print(dp);
  }
  return dp[bageSize];
}
int main()
{
  vector<int> nums = {1,1,1,1,1};
  int target = 3;
  findTargetSumWays(nums,target);
  return 0;
 } 


474. 一和零

题目描述


给你一个二进制字符串数组 strs 和两个整数 mn

请你找出并返回 strs 的最大子集的长度,该子集中 最多m0n1

如果 x 的所有元素也是 y 的元素,集合 x 是集合 y子集

示例 1:


输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
输出:4
解释:最多有 5 个 0 和 3 个 1 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。
其他满足题意但较小的子集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不满足题意,因为它含 4 个 1 ,大于 n 的值 3 。

示例 2:

输入:strs = ["10", "0", "1"], m = 1, n = 1
输出:2
解释:最大的子集是 {"0", "1"} ,所以答案是 2 。


思路分析

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


而m 和 n相当于是一个背包,两个维度的背包。所以本题依旧是一个01背包问题


动归五部曲


确定dp数组以及下标含义

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


确定递推公式

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);


这就是一个典型的01背包! 只不过物品的重量有了两个维度而已。


dp数组初始化

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


dp数组遍历

我们之前讲过背包一定是外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历!


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


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


举例推导过程

以[“10”,“0001”,“111001”,“1”,“0”],m = 3,n = 3为例


最后dp数组的状态如下所示:


image.jpeg

参考代码

#include<bits/stdc++.h>
using namespace std;
//打印dp
void print(vector<vector<int>>& dp,int& m,int& n){
  for(int i = 0;i <= m;i++){
    cout<<"dp["<<i<<"]";
    for(int j = 0; j<=n;j++){
      cout<<dp[i][j]<<" ";
    }
    cout<<endl;
  }
  cout<<"-------------------"<<endl;
}
int findMaxForm(vector<string>& strs, int m, int n) {//m:0个数 n:1个数 
  vector<vector<int>> dp(m+1,vector<int>(n+1,0));//定义dp   dp[i][j]:i个0,j个1的最大子集元素个数. 
  print(dp,m,n);
  for(string str : strs){//外层遍历物体 
    int oneNum = 0;
    int zeroNum = 0;
    for(char ch : str) {//统计当前字符串(物体)的zero和one个数 
      if(ch=='0'){
        zeroNum++;
      }else{
        oneNum++;
      }
    }
    for(int i = m; i >= zeroNum;i--) {//遍历背包体积(二维的:字符串中m和n个数) 
      for(int j = n; j >= oneNum;j--){
        dp[i][j] = max(dp[i][j],dp[i-zeroNum][j-oneNum]+1);
      }
    }
    //print(dp,m,n);
  }
  return dp[m][n];
}
int main()
{
  vector<string> strs = {"10", "0001", "111001", "1", "0"};
  int m = 5;
  int n = 3;
  findMaxForm(strs,m,n);
  return 0;
}

518. 零钱兑换 II

题目描述

给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。

请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0

假设每一种面额的硬币有无限个。

题目数据保证结果符合 32 位带符号整数。

示例 1:

输入:amount = 5, coins = [1, 2, 5]
输出:4
解释:有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

示例 2:

输入:amount = 3, coins = [2]
输出:0
解释:只用面额 2 的硬币不能凑成总金额 3 。

示例 3:

输入:amount = 10, coins = [10] 
输出:1


思路分析


这是一道典型的背包问题,一看到钱币数量不限,就知道这是一个完全背包。


但本题和纯完全背包不一样,纯完全背包是能否凑成总金额,而本题是要求凑成总金额的个数!


注意题目描述中是凑成总金额的硬币组合数,为什么强调是组合数呢?


例如示例一:


5 = 2 + 2 + 1


5 = 2 + 1 + 2


这是一种组合,都是 2 2 1。


如果问的是排列数,那么上面就是两种排列了。


组合不强调元素之间的顺序,排列强调元素之间的顺序。


动规五步曲


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

dp[j]:凑成总金额j的货币组合数为dp[j]


确定递推公式

这个和 494.目标和相似,都是求组合,所以递推公式: dp[j] = dp[j] + dp[j - coins[i]];


dp数组如何初始化

dp[0] = 1 凑成总金额0的货币组合数为1


确定遍历顺序

本题中我们是外层for循环遍历物品(钱币),内层for遍历背包(金钱总额),还是外层for遍历背包(金钱总额),内层for循环遍历物品(钱币)呢?


前面遍历得到的是组合数,后面遍历得到的是排列数


举例推导dp数组

输入: amount = 5, coins = [1, 2, 5] ,dp状态图如下:


0a5aae2da722fba58d38438a8f68de85.jpg

参考代码

#include<bits/stdc++.h>
using namespace std;
int change(int amount, vector<int>& coins) {
  vector<int> dp(amount+1,0);
  dp[0] = 1;
  for(int i = 0; i < coins.size(); i++) {
    for(int j = coins[i]; j <=amount; j++) {
      dp[j]  += dp[j-coins[i]];
    }
  }
  return dp[amount];
}
相关文章
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
248 6
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
166 6
|
Python
【Leetcode刷题Python】剑指 Offer 26. 树的子结构
这篇文章提供了解决LeetCode上"剑指Offer 26. 树的子结构"问题的Python代码实现和解析,判断一棵树B是否是另一棵树A的子结构。
177 4
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
358 2
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
258 3
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
机器学习/深度学习 人工智能 自然语言处理
280页PDF,全方位评估OpenAI o1,Leetcode刷题准确率竟这么高
【10月更文挑战第24天】近年来,OpenAI的o1模型在大型语言模型(LLMs)中脱颖而出,展现出卓越的推理能力和知识整合能力。基于Transformer架构,o1模型采用了链式思维和强化学习等先进技术,显著提升了其在编程竞赛、医学影像报告生成、数学问题解决、自然语言推理和芯片设计等领域的表现。本文将全面评估o1模型的性能及其对AI研究和应用的潜在影响。
332 1
|
索引 Python
【Leetcode刷题Python】从列表list中创建一颗二叉树
本文介绍了如何使用Python递归函数从列表中创建二叉树,其中每个节点的左右子节点索引分别是当前节点索引的2倍加1和2倍加2。
334 7
|
Python
【Leetcode刷题Python】剑指 Offer 22. 链表中倒数第k个节点
Leetcode题目"剑指 Offer 22. 链表中倒数第k个节点"的Python解决方案,使用双指针法找到并返回链表中倒数第k个节点。
191 5
|
Python
【Leetcode刷题Python】剑指 Offer 30. 包含min函数的栈
本文提供了实现一个包含min函数的栈的Python代码,确保min、push和pop操作的时间复杂度为O(1)。
160 4
|
Python
【Leetcode刷题Python】剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
Leetcode题目"剑指 Offer 21. 调整数组顺序使奇数位于偶数前面"的两种Python解决方案,一种是使用双端队列调整数组顺序,另一种是使用双指针法将奇数移到数组前半部分,偶数移到后半部分。
142 4

热门文章

最新文章

下一篇
oss云网关配置