动态规划
动态规划
爱抖腿的嘻嘻嘻
这个作者很懒,什么都没留下…
展开
-
字符串可否拆成字典树中的单词
已知一个非空的字符串和一个包括非空字符的字典list,查找字符串可否根据空格被拆为list中的一个或者多个单词。1.首先第一反应是暴力/回溯,因为就是选字符串中的每个字母要不要,每次dfs都遍历此时的字符串,当此时的字符串前缀发现在字典中有,则dfs剩下的子串,否则一直往前,到最后都没有就返回false,如果在开始发现已经把字符串遍历完了,则返回true。但这样每个字符都要遍历,复杂度为O(nn)2.备忘录:记忆化回溯,把后缀的字符串结果保存到备忘录中,当需要调用的时候就直接返回值。3.动态规划用d原创 2021-04-05 21:23:45 · 171 阅读 · 0 评论 -
二维数组5.不同路径 II 63
查找矩阵中是否有给定路径这个题的就是给一个数组,然后给一个str,看能不能在矩阵中找一条路连成这个str,首先就想到了回溯法,但因为要确定此时的位置,肯定要传入此时的i和j,所以要新建立一个函数core()。然后从第一个位置开始判断他能否作为开局的第一个位置,循环所有节点找,如果都找了还是没有,那就是无解。怎么找呢,首先看传入的字符串长度是不是已经到最长了,到最长说明这个长度之前的全部都匹配了,直接返回true,如果没有,则继续。看这个位置的字符和要求的字符串是否匹配,不匹配直接返回false。如果匹原创 2020-11-12 16:08:26 · 97 阅读 · 0 评论 -
打家劫舍2--解决环形数组的方法
还是给一个数组,只不过他们是首尾相连的,求最大数。则就是第一个要考虑最后一个和第一个之间的关系他们之间有三种关系,全不被抢,选其中一个抢。其实只要选后两种就行了。所以调用两次方法,分别算有没有最后一个和有最后一个两种情况。比较其最大值。class Solution { public int rob(int[] nums) { if(nums.length==1)return nums[0]; return Math.max(core(Arrays.copyOf原创 2021-03-14 19:53:20 · 242 阅读 · 0 评论 -
打家劫舍1
给一个数组,我是一个小偷,目的是偷到最多的钱,规则是偷的时候一家偷完之后下一家不能再偷,否则会触发警报,则求可以偷到最多的钱。求最值,使用动态规划,可以用dp[i]表示0到i可以偷到最多的钱,则返回最后一个dp值即可。dp[i]=dp[i-1]或者是dp[i-2]加num[i],这两个中最大的一个。初值是dp[0]=0,dp[1]=num[0],dp[2]=max(num[1]+dp[0],dp[1])则多设置一个值很重要。class Solution { public int rob(i原创 2021-03-14 19:44:24 · 95 阅读 · 0 评论 -
一维数组字符串5.KMP
首先kmp算法就是找子串的位置,普通做法其实不能用滑动窗口来做,因为一个子串比较不是在一个字符串中找,而是两个字符串,比较失败需要退回,所以每个字符串失败之后还要退回到原来的位置,这种做法效率很低,kmp就是利用有限状态机来指定这个字符串往哪返回而不是返回原来第一个位置的下一个(其实类似动态规划)。所以对于每一个模式字符串p,会创造一个属于它的dp数组,它决定了这次比较字符该往哪走。dp[i][j]表示从p的i位置的字符如果碰到了j这个字符(256长度的ascii码)该往哪走。所以在比较的时候,原字符串不会原创 2021-03-13 18:22:20 · 73 阅读 · 0 评论 -
一维数组字符串4.正则表达式匹配
正则表达式匹配给出一个字符串str和一个正则表达式字符串pattern,求这个字符串是否满足str。这个我就不用动态规划做了,就用普通的方法主要有.和*两种符号,.表示任何字符,*表示前面的字符可以出现任意次。总体思路就是两个字符串往前推进,如果一个到最后的时候另一个也到最后,则说明匹配成功。然后分情况为第二个字符为*和不为*两种,因为其实.表示的意思和普通字符是一样的,只不过普通字符是一样才推进,而.是所有的都能推进。进入算法之后先看是否为null,为null肯定为false,然后进入递归如果此原创 2020-11-16 16:08:31 · 600 阅读 · 0 评论 -
子序列7.最长回文子序列
在回文子序列中,dp[i][j]定义的是数组i到j中最长的回文子序列的长度,如果此时知道了dp[i+1][j-1],则s[i]和s[j]如果相等,则dp[i][j]=dp[i+1][j-1]+2如果不同则他俩可能同时出现,则选一个最大的dp[i][j]=max(dp[i+1][j],dp[i][j-1]),如果只有一个字符,也就是i和j相同时肯定为1,则中间这一列为1,求一个位置需要先求出它的下,左下,左三个位置,最终要求的为dp[0][nums.length-1],从最后一行第一个开始遍历吧,从左到右,原创 2021-03-10 16:05:47 · 105 阅读 · 0 评论 -
子序列3.俄罗斯套娃信封问题
给你一个二维整数数组 envelopes ,其中 envelopes[i] = [wi, hi] ,表示第 i 个信封的宽度和高度。当另一个信封的宽度和高度都比这个信封大的时候,这个信封就可以放进另一个信封里,如同俄罗斯套娃一样。请计算 最多能有多少个 信封能组成一组“俄罗斯套娃”信封(即可以把一个信封放到另一个信封里面)。这实际上是一个最长递增子序列的问题。要把小信封装进大信封,问能套几次,给了信封的长和宽数组,其实这是两个维度的最长递增子序列,因为有两个因素,所以先按照宽来升序,如果宽相同则按照原创 2021-03-10 14:14:43 · 112 阅读 · 0 评论 -
01背包3.目标和494
给定一个非负整数数组,a1, a2, …, an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。返回可以使最终数组和为目标数 S 的所有添加符号的方法数。这个题的选择就两种,做加法或者做减法,所以也不用写循环了,写两个就行了。但用回溯始终是一个递归问题,比较低效,所以最好是利用重叠子问题也就是用动态规划来做,我们把nums划分为两个子集A和B,分别代表分配+的数字和分配-的数字,则可以得出A-B=S A=S+B 2A=S+原创 2021-03-09 16:09:08 · 108 阅读 · 0 评论 -
完全背包2.换钱的最少组合数322
求可兑换的组合数量。注意必须等于amount而不是小于等于。与01背包问题不同的是,01背包考虑的是这个物品是否可拿,而这个问题是这个物品是否可拿,拿的话可以无限拿直到装不下。因为每个硬币都能选多个,所以要遍历每个硬币的每个选择,不像上个题只要找到最小的硬币数就可以,所以尽可能是选择比较大的 硬币,这样可以有最小的数量。直接遍历每个amount,然后在每个amount中选一个就行。而这个是要所有的组合,所以每个都要尽可能多的选择。所以首先遍历每一个硬币,然后尽可能多的选择硬币的个数,穷尽每一种选择d原创 2021-02-27 17:40:32 · 78 阅读 · 0 评论 -
01背包2.1和0
给一个二进制数组,要求返回其最多的子集数目,要求返回的子集中0的个数不大于m,1的个数不大于n。分析一下可以知道这其实也是一个是否选数组中元素的题,每个元素只能选一次,限制条件是0和1个个数,所以可以看成是一个01背包的问题。dp[i][j][k]表示的是在数组0到i中,可以拥有的最大子集数,其中j和k代表此时的0和1的个数。dp[i][j][k]如果此时选i,则等于dp[i-1][j-q][k-w]+1如果不选则是dp[i-1][j][k]有两个需要注意的是ijk都要多设置一行,这样可以避免原创 2021-02-27 12:12:47 · 63 阅读 · 0 评论 -
01背包1.分割等和子集474
把一个数组分成两部分,是他们的和相同,这就是典型的分成两部分的01背包问题。可以想到两个数相同,也就是这个数是数组和的一半,是一个偶数。与01背包的区别是01背包需要保证值小于规定值,而这个题需要恰好等于规定值的一半。特点是每个数只能用一次,所以只能慢慢去添加容量。用一个二维数组来表示,行数就是物品的数量,表示每次考虑一个物品,列数表示背包容量+1,因为我们需要为0的背包容量dp[i][j]就是是否可以从0到i的区间中取出总量为j的物品,也就是它是一个布尔值。有两种选择就是是否选num【i】这个位置的原创 2021-02-27 10:51:08 · 88 阅读 · 0 评论 -
打家劫舍3.二叉树递归
就是一个树形动态规划,实现直接相连的节点之间不能都偷,然后找到如何偷到最大金额。比如说现在有爷爷,两个孩子,四个孙子,那么爷爷需要的最多的就是可能就是爷爷的钱加四个孙子的钱或者是两个儿子的钱的和,儿子和孙子的钱通过递归来算,这样其实最终仍然是通过递归从底往上上的。不如直接从底向上来动态规划。如果使用的是数组,则可以直接存储从小开始算,但树是没法存的,所以用一个map来存储每个节点对应的最大金额,这样在算的过程中就不用重复算了。然后换一个思路可以和数组一样选择每个节点偷不偷,如果偷了,则儿子都选择不偷的原创 2021-02-22 22:26:24 · 218 阅读 · 0 评论 -
一维数组字符串3.完全平方数
完全平方数就使用动态规划来计算每一个可以到的位置在进入j之前要让dp[i]初始化为i,也就是最多是每个位置都是1.class Solution { public int numSquares(int n) { int[] dp=new int[n+1]; dp[0]=0; for(int i=1;i<=n;i++){ dp[i]=i; for(int j=1;i-j*j>=0;j++){原创 2021-02-21 08:42:57 · 206 阅读 · 0 评论 -
不同的二叉搜索树的数量
求1到n有几种二叉搜索树的组合方式二叉搜索树也就是二叉查找树,中序遍历的顺序是从小到大。可用动态规划解决,因为如果知道1到n-1的二叉搜索树数量,可以递推出1到n的二叉树数量,因为要把每一个节点i当成根节点,然后1到i-1作为左子树,i+1到n作为右子树。所以用G(n)表示共有几种,f(i,n)表示以i为节点,1到n有几种,则把所有的f加起来就是G(n),而i的左子树的二叉搜索树数量和右子树的二叉搜索树数量的笛卡尔积就是可组合的数量。所以f(i,n)=G(I-1)G(n-i),其中g(0),g(1原创 2021-02-07 14:19:14 · 343 阅读 · 0 评论 -
子序列5.编辑距离
有两个字符串a和b,可把a变成b的最小的操作数有多少。可以由替换,插入,删除三种操作。两个单词就有六种方法,但有一些是等价的,所以其实就三种分别是在A中插入字符,在B中插入字符,修改A的字符。用dp[i][j]表示A的前i个字母和B的前j个字母之间的编辑距离所以得到dp[i-1][j],dp[i-1][j-1],dp[i][j-1]三个值之后,他们分别对应三种操作后A和B一样,如果只有一个少1,则可以通过增加另外一个的方式来是他们一样。如果是dp[i-1][j-1]则如果i和j位置的值相同,则就不原创 2021-02-03 14:38:09 · 133 阅读 · 0 评论 -
完全背包1.换钱的最少货币数322
给一个钱金额的数组和一个目标值,可以无限取钱的数量,求最少的可以达到目标值的硬币数。因为硬币的数量是无限的,子问题之间没有限制,所以具有最优子结构,可以以用动态规划来解。求f(i)可由f(i-1)得到,要得出状态转移方程,(1)首先确定基础例子,也就是target为0时k也为0、(2)然后确定状态,也就是原问题和子问题之间变化的量,也就是target值。(3)接着确定选择,也就是导致状态变化的行为,是目前硬币的count(4)dp函数的意义,一般其中的参数是原问题和子问题之间变化的量,而值本身是原创 2021-01-31 15:50:13 · 127 阅读 · 0 评论 -
二维数组4.求全为1正方形221/1277
求最大正方形f[i][j]表示的含义有两个,第一是以ij结尾的正方形的边长,第二是以这个点为右下角结尾正方形的个数 ,为什么它可以代表个数,因为都全1了,所以其他更少长度为边长的正方形肯定更定也是存在的。所以可以得出dp[i][j]为其他周围三个点的最小值,因为只有全为1他才可能为1.按照这个即可求出,class Solution { public int maximalSquare(char[][] matrix) { int[][] dp=new int[matrix.le原创 2021-01-27 14:11:35 · 291 阅读 · 0 评论 -
子序列4.最长公共子序列LCS
对于两个字符串的动态规划问题,就是建立一个二维数组dp[i][j]的意思就是arr1[0]到arr1[i]和arr2[0]到arr2[j]他们的最长公共子序列的长度是dp[i][j]第一行和第一列肯定都是初始化为1的。选择状态方程时其实就是选择每个字符要还是不要,这个题选择的关键就是这个字符要都存在于两个字符串中。所以遍历条件就是是否相等,相等则dp[i][j]=dp[i-1][j-1]+1;不相等则p[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);有一个非常好用的原创 2021-01-27 12:43:01 · 70 阅读 · 0 评论 -
子序列1.最长递增子序列
求的是子序列而不是子数组,利用滑动窗口显然解不了,应该使用动态规划。利用f(i)表示以第i个元素为结尾的递增子序列的长度。则f(i)=max(f(j))+1;能添加j的要求就是nums[j]<nums[i];所以每求一个值都要遍历前面的值来求前面符合要求而且最大的值。class Solution { public int lengthOfLIS(int[] nums) { if(nums.length==0)return 0; int[] dp=new原创 2021-01-27 10:23:58 · 121 阅读 · 0 评论 -
二维数组3.最小路径和
与不同路径相似,不同在于初始化和状态方程的不同。class Solution { public int minPathSum(int[][] grid) { int[][] dp=new int[grid.length][grid[0].length]; dp[0][0]=grid[0][0]; for(int i=1;i<grid.length;i++){ dp[i][0]=grid[i][0]+dp[i-1][0];原创 2021-01-26 19:54:00 · 146 阅读 · 0 评论 -
二维数组2.不同路径
求一个机器人从(0,0)走到(m,n)的路径数量,只能往下或者往右移动一步。f(i,j)表示从(0,0)走到(i,j)的路径数量,则f(i,j)=f(i-1,j)+f(i,j-1),画图可以很轻松的看出怎么求每个位置和初始赋值,第一行和第一列全都是1,然后求出即可。这样的时间复杂度为O(mn),空间复杂度为O(mn)class Solution { public int uniquePaths(int m, int n) { int[][] dp=new int[m][n];原创 2021-01-26 13:09:48 · 199 阅读 · 0 评论 -
子序列6.最长回文子串
1.暴力解从i=0和j=i+1开始枚举所有的子串,然后在每一个子串中都利用方法来判断它是否是一个回文串。判断的过程可以进行剪枝,也就是如果长度不大于上一次的s就不判断。判断方法可以利用双指针法。这种复杂度为O(n3)2.从中心扩展可以从两边开始枚举,也可以从中心开始枚举,判断以它为中心的字符串是否是回文串。分为偶数串和奇数串,只需要进行一次for循环,遍历字符串,遍历到一个位置的时候判断以这个位置为中心的奇数串和偶数串能够得到的最大子串。这样每个位置只需要遍历一遍,在遍历的过程中又遍历一遍,所以是O原创 2020-12-27 10:17:51 · 91 阅读 · 1 评论 -
一维数组字符串2.最长有效括号
有一个只包含()的字符串,找出其中最长的有效的括号子串。栈利用栈,我们遇到(的时候将它的下标放入栈中,遇到)时先将栈顶元素弹出(也就是它的左括号),如果此时栈中没有元素,则说明此时之前的所有括号都被分配完,这就是最后一个匹配的有效右括号,将它的坐标放入栈中。如果此时栈不为空,将res更新为以此时右括号结尾的字符串的长度,为原来res和i-stack.peek()的较大值。class Solution { public int longestValidParentheses(String s)原创 2021-01-20 16:32:40 · 151 阅读 · 0 评论 -
买卖股票2.买卖股票的时机(可多次交易)
可无数次交易(必须交易完成后再进行)因为可以多次交易,所以和1的区别只有状态方程不同如果今天结束后持股,则可能是昨天没持股,今天持股,也可能是昨天就持股如果今天结束后不持股,则可能是昨天持股,今天卖了,也可能是昨天就不持股。class Solution { public int maxProfit(int[] prices) { int len = prices.length; if (len < 2) { return 0;原创 2021-01-16 01:07:09 · 363 阅读 · 0 评论 -
买卖股票1.买卖股票最佳时机
求一个数组中买卖股票得到的最大利润‘遍历数组,因为只能先买后卖,所以每次遍历都更新此时的最小值,更新最小值之后不能卖,如果没有更新最小值,则看此时的价格卖能不能得到最大利润,进行比较。class Solution { public int maxProfit(int[] prices) { if(prices==null || prices.length==0 || prices.length==1)return 0; int maxpro=Integer.MI原创 2021-01-15 23:29:53 · 58 阅读 · 0 评论 -
斐波那契1.斐波那契数列
斐波那契数列有很多类似的问题,比如说跳台阶,矩形覆盖,变态跳台阶,只要记住公式就可以算出用递归非常简单,但一般更建议使用循环循环就是自下而上进行计算public class Solution { public int Fibonacci(int n) { if(n==0){ return 0; } if(n==1){ return 1; } int res=0;原创 2020-11-11 21:01:51 · 203 阅读 · 1 评论 -
一维数组1.把数字翻译成字符串
一个数字可以通过1到25翻译为26个字母,也就是一个数字可以作为单独的一位翻译,也可以作为两位数其中的一位翻译,当然前提是这一位数为1或者是2.求一个数字可以被翻译的种类个数。看这个题就可以用递归来分析一个数比如12323可以先判断这一位是不是1或者2,如果不是则判断下一位,如果还不是就一直往下判断,如果都没有1和2则把这个f变成1.则f(12323)=f(2323)+f(323)=f(323)+f(23)+f(32)+1=f(23)+2(f(32)+1)=2+2+2=6.这种就是递归的思路但这种原创 2020-12-05 13:33:44 · 174 阅读 · 0 评论 -
二维数组1.礼物最大价值
在一个m*n的棋盘上每格都有一个礼物,价值都大于0,从左上角开始拿格子里的礼物,直到到右下角,求最大价值的礼物为多少。求最大价值的礼物,二维矩阵首先想到这是不是回溯法,但并没有出现失败的情况,因为不到最后根本不知道哪个最大,所以不是回溯法。求f(i,j)就是在i,j这步时可能拿的最大价值,这一想就是一个没有后效性,可以用递归和动态规划解的问题。首先用递归来思考,如果f(i,j)要不往下走要不往右走,所以可能是f(i+1,j)+arr[i,j]或f(i,j+1)+arr[i,j] 所以直接用动态规划从下原创 2020-12-05 14:30:35 · 205 阅读 · 0 评论 -
子序列2.连续子数组的最大和
求数组中连续子数组的最大和首先的思想就是枚举所有的子数组然后求最大和,但子数组的长度可能是1到n,求长度为i的子数组需要遍历一遍数组,也就是复杂度为O(n2)。分析可以得到如果此时和为负数,那加了下个数一定是变小了。所以遍历的时候如果cur小于0,就舍弃cur并令它等于当前的arr[i]。大于0的话就cur加上当前数。最后如果cur大于max,就令max=cur。遍历到最后即可知道哪个是最大的。0x80000000可以表示无穷小。public class Solution { public原创 2020-12-03 16:05:54 · 208 阅读 · 0 评论