我的算法时间记录二

二、动态规划类

2020.8.15 小杨缓慢但前进 

  • 爬楼梯 :

先进思想:

开始我想的递归是一个时间复杂度在2的阶乘级别,超出时间限制,是因为里面有很多的重复计算。递归不是不能用,在重复的这块进行优化就可以,把计算过的存储起来,遇到就直接查。后来我采用的动态规划,开始填一维表,时间复杂度是减下来了,但仍然存在空间上的优化。可以直接使用两个变量,一步步的赋值。仅采用常数级别的空间。

本来我以为到此就为止了,这个题印出来非常厉害的数学解法。根据递推式写出矩阵相乘的形式,就能直接求出f(n)的表达式,其中涉及如下矩阵运算,通过对角化满秩矩阵将矩阵的N次方转化为对角矩阵的n次方,而对角矩阵的n次方直接等于对角线上的元素的n次方,一下子就简化不少:

在这里,我们可以直接用数学公式去求解,一步到位,一行代码中的pow运算会有logn级别的时间复杂度,因此时间复杂度被降低到logn级别,空间复杂度是常数级别。我们发现,在前期做越多的数学分析,就能更大程度地降低问题复杂度。也就是人帮机器解决的问题越多,人的思考越多,机器的“思考”就能越少,就越高效。

动态规划类的题目一般都能写成这种递推式,递推式的只要是线性的(非线性也能转化为线性,只需要变化一下函数,加个常量啥的),就能使用这种数学思路求解。在编程实现上,由于具备连续性,因此可以不用开辟新数组,而是用常数个变量进行滚动赋值即可,但是要十分注意变量赋值的顺序。

  • 打家劫舍:

先进思想:

还是那个经典的动态规划方法,就是填表法。格外注意一下特殊值的返回和初始值的设置。

另外有一种新思路,就是针对这种可以“滚动”的数值变化,我们可以使用两个单变量来互相更新,first变量的当前值是second的前一个值,我本身使用的是原数组的更新,不需要额外的空间。

  • 区域和检索:

先进思想:

一开始没写过这种类,就直接看答案了,这个的意思是初始化在创建对象的时候就要懂点脑筋,因为sumrange函数需要被调用多次,如果初始化的时候仅仅是单纯的初始化,将对象和nums数组联系起来的话就会导致sumrange函数多次使用消耗的时间资源和空间资源过大。因此在初始化的时候,就要构建利于sumrange计算的数据结构。本题中求区间和,那么就可以构建一个累计和的列表,这里用到动态规划的思想。

  • 除数博弈:

先进思想:

感觉这是个博大精深的数学问题,就列举了一些情况之后没做大胆猜测。后来看到那一行代码,我觉得有点可惜。看了题解的证明之后,心服口服。首先我们通过找规律可以发现,当N是偶数的时候,先手胜;当N是奇数的时候,先手败。下面用数学归纳法证明。

(1)假设N<=k时,当N是偶数的时候,先手胜;当N是奇数的时候,先手败。

(2)讨论N=k+1的情况:

当k+1为奇数时,对于先手Alice来说,这时候选的因数只能是奇数(因此这个先手其实是没有优势的),因此导致下一个N必为偶数(奇数-奇数)。此时Bob成了先手,而此时的N<=k了,满足(1)中条件,这个时候的先手Bob必胜。

当k+1为奇数时,对于先手Alice来说,这时候因数是可能为奇或者为偶的,因此有了选择的余地,当然一定要选自己能赢的选择。选择一次之后,肯定满足(1)中条件,因此可以利用(1)中结论让自己必赢。只需要选偶数因数,使得下一个N为奇数,从而使得此时的先手Bob必败。

  • 连续数列和:

 

先进思想:

这个题目之前在做数组类的时候遇到过, 思路还是那个思路,构造连续和,但是连续和有讲究,还要根据前面的连续和的政府来决定,如果为正就直接加,否则为负我还加啥加。

  • 按摩师:

做过,那个经典的不相邻动态规划问题,nums[i]=max(nums[i-2]+nums[i],nums[i-1])

  • 三步问题:

先进思想:

还是经典的台阶问题:nums[i]=nums[i-1]+nums[i-2]+nums[i-3],但是可以不用开辟新的数组空间,利用“滚动性”只需要设置三个变量即可。

for i in range(4,n):
    a,b,c=b,c,(a+b+c)%1000000007

至于为啥要对这个奇怪的数取余,可能是保证了在某个数据范围内降低数值但不会引起数据冲突从而达到简化操作数的目的。

  • 使用最小花费爬楼梯:

先进思想:

动态规划的厉害之处在于记录了过往的最优解,也就是子问题的最优解。而这种“轨迹类”问题需要记录子问题的最优解之后,整个问题的最优解在子问题的最优解的积累下诞生,往往最优解的答案是有明确指向的,注意搞清楚最优解的诞生范围,这是这个题带给我的关于动态规划的新知识点。

  • 不同的二叉搜索树:

先进思想:

热评:“假装思考,查看题解,上交代码,关闭leetcode,打开csdn。”

又是一道难倒我的数学题。要求这些数能构成二叉搜索树的个数,首先我们来看是如何构成二叉搜索树的?两步:第一步选定根节点,第二步,比根节点小的数构成根节点的左子树,比根节点大的数构成右子树。依次类推,那么比根节点小的这些数也是按照上述方法去构成二叉搜索树的,那么以当前的根节点能构成的二叉搜索树的个数就等于左子树个数*右子树个数。

对于1-n来说,以当前的i为根节点构成的二叉搜索树的个数为F(i,n)=G(i-1)*G(n-i),而我们要求的是G(n),G(n)等于从1到n的数每个数都当一次根节点的个数之和。

组合数学里面,这个G(n)就是大名鼎鼎的卡特兰数,有现成的递推公式和求和公式,如果知道卡特兰数的话,代码就从二重循环变成一重循环了......

  • 一和零:

先进思想:

这居然也是背包问题??后来看到二维表的我眼泪掉下来。递推式在于dp[i][j]=max(dp[i][j],1+dp[i-count0][j-count1])。两重循环,外层循环是对数组的每个字符串进行遍历,关键点在于内层循环对能装的背包进行遍历,因此需要逆序遍历。

  • 最长上升子序列:

先进思想:

最近做的填表项目都开始变难,首先是需要多次遍历。至少是两重循环。另外如果是二维数组的话,就更容易混了。

今天的我彻底理解了,是积累式的螺旋式上升。只要当前的这个数,比前面的数要大的,就可以直接加,当然加的时候需要比较,选最大的那个加。

  • K站中转内最便宜的航班:

先进思想:

官方题解那是人写的吗?我好不容易建立的自信心在官方题解面前一败涂地。还好我不死心,找到一篇能理解到位的。

对于这样的正权值的有向无环图,正好用dijkstra算法!复习一下,dijkstra算法就是每次找没有更新的最便宜的结点进行更新。
但是这个数据结构好复杂,要用三个字典和一个列表。

还是填表的亲切。
这个就很妙,建立n*K的表格。dp[k][站点]的意义是从起点到改站点中间经历k站的开销。

  • 新21点:

先进思想:

最灵魂的就是这张图。最终的答案就是求dp[0],从0开始之后经历各种情况之后得分小于N的概率。另外就是注意N和K-1+W的关系导致边界不同,做个比较就行。计算的时候注意利用重叠性。下一次计算只需要去掉尾巴再加上当前数字作为开头就行。 

 

  • 剪绳子:

先进思想: 

又是一个数学问题。
已知若干个数的和,求若干数的最大积。
解决这个问题分为两步,第一步,证明这若干个数必须相等。第二部,证明这若干个数必须是3。利用到了算术平均不等式。

因此在和一定时,积最大的时候是这若干个数相等的时候。
在这若干个数相等的情况下,我们去求这个积。

因此这若干个数还必须每个都等于3.

确定这两个问题之后,在最后求解的时候依然还需要进一步优化,因为不全是3这种最优的情况。
不能整除3的话,分两种情况讨论,一种是余数为2,一种是余数为1。
无论是哪种情况,我们都有三种选择,要么拆掉这个余数,要么保持这个余数,要么把这个余数和3合并。
因此只需要分别计算这三种情况的乘积后选择最大即可。
余数为2时,最大的是保持2不变;余数为1时,最大的是和3合并为4。

最后记得对1000000007求余。

  • 矩阵区域和:

先进思想:

#暴力算法不顶用 但是思想是对的 就是对于矩阵和的范围是应该是大于0 另外是小于mat行数和列数
#求矩阵的任一区域和都可以使用首先建立矩阵P之后使用容斥原理来实现 其中矩阵P[i][j]的意义是(0,0)到(i-1,j-1)的矩阵和 第0行和第0列的P均为0

核心公式:ans[i][j]=get(i+K+1,j+K+1)-get(i-K,j+K+1)-get(i+K+1,j-K)+get(i-K,j-K)

  • 只有两个键的键盘:

先进思想: 

不得不说官方题解也写得没有这个好。
请看:
https://leetcode-cn.com/problems/2-keys-keyboard/solution/cong-di-gui-dao-su-shu-fen-jie-by-fuxuemingzhu/

转化成了递归问题,一步步缩小问题规模。
之所以可以这样,是因为发现了这样的规律:如果是质数n,那么就只能通过复制1次,然后粘贴n-1次,总共就是n次。如果是n=i*j也就是能再分解的,minsteps(n)=minsteps(i)+j,对于j来说就是j次。

  • 买卖股票的最佳时机含手续费:

先进思想:

状态转换的思想很好,dp数组加上了第二维的状态变量0/1,0是不持有,1是卖出。然后状态要么是前一天延续而来,要么是前一天是相反的状态转变而来。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值