算法学习——LeetCode力扣动态规划篇3(494. 目标和、474. 一和零、518. 零钱兑换 II)

本文主要是介绍算法学习——LeetCode力扣动态规划篇3(494. 目标和、474. 一和零、518. 零钱兑换 II),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

算法学习——LeetCode力扣动态规划篇3

在这里插入图片描述

494. 目标和

494. 目标和 - 力扣(LeetCode)

描述

给你一个非负整数数组 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

提示

1 <= nums.length <= 20
0 <= nums[i] <= 1000
0 <= sum(nums[i]) <= 1000
-1000 <= target <= 1000

代码解析

这道题根本还是回到了在数组中找到一个集合,使得该集合与其余部分之差为target,通过公式推导:
我们可以知道 该集合的值为:(sum-target)/2;

回溯法

目的是找到和为**(sum-target)/2** 的种类

class Solution {
public:int result = 0;void backtracking(vector<int>& nums, int target ,int deep ,int sum){if(sum > target)return;if(sum == target)result++;if(deep == nums.size()) return;//从任一点开始for(int i= deep ; i < nums.size() ;i++){backtracking(nums,target , i+1  , sum + nums[i]);}return;}int findTargetSumWays(vector<int>& nums, int target) {int sum = 0 , diff = 0;for(auto it:nums) sum += it;diff = sum - target;if( diff<0 || diff%2==1 ) return 0;//回溯找diff/2backtracking(nums,diff/2,0 ,0);return result;}
};
动态规划
  1. 背包定义: dp[i][j] , i是使用0-i的元素,j是背包容量,dp[i][j]是使用这么多个元素恰好凑成j的情况
  2. 初始化:dp[0][0]为1,装满容量为0的背包,有一种方法。dp[0][j],看第一个元素的大小情况,进行赋值1(如果第一个元素为0.则dp[0][0]应该为2),其他层的根据第一层改变.
  3. 遍历顺序:从上往下
  4. 递推公式: dp[i][j]=dp[i-1][j](不需要num[i]就能够凑出j的情况)+dp[i-1][j-nums[i]];(需要num[i]凑出j空间的情况) 最终就能实现,从0-i元素当中组合,得到target的所有情况。
class Solution {
public:int findTargetSumWays(vector<int>& nums, int target) {int sum = 0 , diff = 0;for(auto it:nums) sum += it;diff = sum - target;if( diff<0 || diff%2==1 ) return 0;vector<vector<int>>  dp( nums.size() , vector<int>(diff/2 + 1 , 0) ) ;dp[0][0] = 1;for(int j=0 ; j<(diff)/2+1 ; j++)if(j==nums[0]) dp[0][j] += 1;for(int i=1 ; i<nums.size() ;i++){for(int j=0 ; j<(diff)/2+1 ; j++){if(j>=nums[i])dp[i][j] = dp[i-1][j] + dp[i-1][ j - nums[i]] ;elsedp[i][j] = dp[i-1][j];}}      return dp[nums.size()-1][(diff)/2];}
};

474. 一和零

474. 一和零 - 力扣(LeetCode)

描述

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

请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。

如果 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 。

提示

1 <= strs.length <= 600
1 <= strs[i].length <= 100
strs[i] 仅由 ‘0’ 和 ‘1’ 组成
1 <= m, n <= 100

代码解析

动态规划(01背包,三级数组)

和经典的背包问题只有一种容量不同,这道题有两种容量,即选取的字符串子集中的 0 和 1 的数量上限。

经典的背包问题可以使用二维动态规划求解,两个维度分别是物品和容量。这道题有两种容量,因此需要使用三维动态规划求解,三个维度分别是字符串、0的容量和 1 的容量。

定义三维数组dp,其中dp[i][j][k] 表示在前 i 个字符串中,使用 j 个 0 和 k 个 1 的情况下最多可以得到的字符串数量。
当 0 和 1 的容量分别是 j 和 k 时,考虑以下两种情况:

  • 如果 j< zeros 或 k<ones,则不能选第 i 个字符串,此时有 dp[i][j][k] = dp[i−1][j][k];

  • 如果 j ≥ zeros 且 k ≥ones,则如果不选第 i个字符串,有dp[i][j][k]=dp[i−1][j][k],如果选第 i个字符串,有 dp[i][j][k]=dp[i−1][j−zeros][k−ones]+1,dp[i][j][k] 的值应取上面两项中的最大值。

因此状态转移方程如下:
在这里插入图片描述

class Solution {
public:int find_0(string s1){int num = 0;for(auto it:s1) if(it == '0') num++;return num;}int findMaxForm(vector<string>& strs, int m, int n) {vector<vector<vector<int>>>  dp( strs.size() ,vector<vector<int>>( m+1 ,vector<int>( n+1,0) ));int num_0 = 0,num_1 = 0;num_0 = find_0(strs[0]);num_1 = strs[0].size() - num_0;for(int j=0 ; j<= m ;j++){for(int k=0 ; k<= n ;k++){if( j>= num_0 && k>= num_1)dp[0][j][k] = 1;}}for(int i=1 ; i<strs.size() ; i++){num_0 = find_0(strs[i]);num_1 = strs[i].size() - num_0;for(int j=0 ; j<=m ;j++){ for(int k=0 ; k<=n ;k++){if( j>= num_0 && k>= num_1)dp[i][j][k] = max( dp[i-1][j][k], dp[i-1][j - num_0][k - num_1] + 1);elsedp[i][j][k] = dp[i-1][j][k];}}}int max_num = 0;for(int i=0 ; i<strs.size() ; i++){if(dp[i][m][n] > max_num) max_num = dp[i][m][n];// cout<<dp[i][m][n]<<' ';}return max_num ;}
};
动态规划(滑动数组,二级数组)

由于dp[i][][] 的每个元素值的计算只和dp[i−1][][] 的元素值有关,因此可以使用滚动数组的方式,去掉 dp 的第一个维度,将空间复杂度优化到 O(mn)O(mn)。

实现时,内层循环需采用倒序遍历的方式,这种方式保证转移来的是 dp[i−1][][] 中的元素值。

class Solution {
public:int find_0(string s1){int num = 0;for(auto it:s1) if(it == '0') num++;return num;}int findMaxForm(vector<string>& strs, int m, int n) {vector<vector<int>>  dp( m+1 ,vector<int>(n+1,0));int num_0 = 0,num_1 = 0;for(int i=0 ; i<strs.size() ; i++){num_0 = find_0(strs[i]);num_1 = strs[i].size() - num_0;for(int j=m ; j>=num_0;j--){ for(int k=n ; k>=num_1 ;k--){dp[j][k] = max( dp[j][k], dp[j - num_0][k - num_1] + 1);}}}return dp[m][n] ;}
};

518. 零钱兑换 II

518. 零钱兑换 II - 力扣(LeetCode)

描述

给你一个整数数组 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

提示

1 <= coins.length <= 300
1 <= coins[i] <= 5000
coins 中的所有值 互不相同
0 <= amount <= 5000

代码解析

完全背包

一看到钱币数量不限,就知道这是一个完全背包。
dp[j]:凑成总金额j的货币组合数为dp[j]

dp[j] (考虑coins[i]的组合总和) 就是所有的dp[j - coins[i]](不考虑coins[i])相加。
所以递推公式:dp[j] += dp[j - coins[i]];

首先dp[0]一定要为1,dp[0] = 1是 递归公式的基础。
从dp[i]的含义上来讲就是,凑成总金额0的货币组合数为1。

class Solution {
public: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=0 ; j<=amount ; j++  ){if( j>=coins[i] )dp[j] +=  dp[j-coins[i]] ;elsedp[j] = dp[j];}}return dp[amount];}
};

这篇关于算法学习——LeetCode力扣动态规划篇3(494. 目标和、474. 一和零、518. 零钱兑换 II)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/866953

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

哈希leetcode-1

目录 1前言 2.例题  2.1两数之和 2.2判断是否互为字符重排 2.3存在重复元素1 2.4存在重复元素2 2.5字母异位词分组 1前言 哈希表主要是适合于快速查找某个元素(O(1)) 当我们要频繁的查找某个元素,第一哈希表O(1),第二,二分O(log n) 一般可以分为语言自带的容器哈希和用数组模拟的简易哈希。 最简单的比如数组模拟字符存储,只要开26个c

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖