今天的主要技巧
滑动窗口,很多O(N)的思路就是这么来得
打表,简单格式的输入输出。
预处理数组/结构,省重复连续遍历。
题目一:
初始思路:假设绳子右侧点正好压住了这个点,然后看左边压了多少个点,这个是一个小的贪心策略
那我们要怎么实现这个思路:遍历吗?找一个>=绳子左距离的最短的点,可以用二分,找到之后就可以直接计算出来右边压在这个点能覆盖的点的数量
这个思路的时间复杂度N*logn
换个思路:有没有更好的方法,有。就是类似滑动窗口的思路。叫绳子的左边来压到每一个点。然后叫右边的串口去动,可以直接照着点去移动,然后确定绳子长度不超标就行,这里的实现方式可以理解为双指针(毕竟这两个确实是一个东西),这样的优势在于不会回退
所以时间复杂度:O(N)
来看看题目2:
看起来想找硬币那道题,可以用dp来做,不过也有不同,这道题我们最后可以优化的更好。
(ps:其实这道题>24个的偶数都能有结果)
我们思路1:暴力尝试,我们先看8个的袋子,最多要用多少个,然后每一次减一个,看6个的袋子能不能装剩下的,如果能正好装好,就直接OK,返回结果。一路见到8个的袋子没有了,要是还没有结果就返回-1。
这个思路可以优化吗?可以,我们不用试所有的8个袋子个数的类型。实际上,当我们一个个的减少八个苹果袋子的数量,导致留给六个的袋子的苹果数量>24个的时候,就没有必要去试了。因为这个时候我们会发现循环。因为将超过的部分分给八个的袋子之后,剩下的交给六个的袋子的苹果数你一定之前遇到过。
为什么,因为这样想,如果超过了24个,你用六个的袋子就算出结果了,你觉得能对吗?如果有结果,你一定可以用八个的先装好!!!
这个看起来很复杂,但是实际上是O(1)的方法,因为我们循环不了几次就出结果了。
这个思路很nb了对吧,但是这个思路本身是具有很强的局限性的。换个题目就不好使了。
但是这个时候我们说,当我发现一个题目,入参是整数,出参也是整数,我们可以一开始用一个很蠢很粗暴直接的方法,然后发现了规律之后,再去进行优化。
这个就是之前这个思路的代码的结果,发现规律了吗?
这个就是打表法,原理?思路?逻辑?爱谁谁,我就问这个规律做出来对不对吧?(ps:对)
甚至我还可以大胆的说,这个绝对绝对是最优解。还能有比这个更简单更快的吗?
另外补充一句,网上那些搞数学规律解题法的基本都是,先自己打表打出来结果,再去想数学的解释方式,最后写出一个基本看不懂的帖子来装逼。这种人下次见了就别惯着。
不是所有int->int的题目能这样,但是这样的题目确实是不罕见。约莫能有四成。
我们再来一个题目:
先后手两个动物来吃草,他们只能吃4^n的草并且不能不吃,我们现在有n份草,谁先吃完谁就赢了。问谁能赢?
好了,请问粗暴的代码怎么写?
前面几份草很简单,我们自己分析一下就行了。我们可以简单分析一下前面0-4的情况。
往下怎么做?
我们就问当前我吃1,4,16…份,问能不能找到一条路让我赢。找到了,就赢了。所以我们可以直接递归求,记得这个时候我们先后手转换了,每一次当前吃的4,看剩下的草自己转后手有没有机会。
在递归的时候注意一下,小心4之后溢出了。如果32位成了1,成了负数就是会无限循环了。所以我们要和N/4比较。
没有,那就是对面赢了
好了,我们把草从0-50份,用这个思路出一下结果。这个明显吗?
对吧,后先后先先的一个规律。
只要输入输出格式简单一些。我们就可以这样来看一下思路。
我甚至可以想到网上那些帖子的嘴脸了,一个个都是大数学家。给你一通分析,真的是。。。
我们再来看一个题目:
补充说明一下,这里的这个左侧是一个虚指,你可以叫左边两个是左侧,也可以叫整个是左侧,也可以整个都是右侧,左侧为空。
所以,我们可以枚举左侧部分的大小情况。统计一下有多少个G,染成R就可以了
这个是一个O(N^2)的方法,我们需要遍历两轮嵌套。但是里面的东西是一样的,我们来回来去遍历,实在是浪费。
那么我们能不能改善一下呢?我们可以设置一个数组,统计0-i范围上,R的数量,再做一个数组,统计i-N-1上,G的数量,有了这个,我们就不用每一轮都遍历一遍整个的了。直接取数组的数据就可以了。
这个就是数据的预处理。用空间换时间,把一些经常要遍历的数据我们通过存储遍历结果,省去后续还需要的空间。
再来一个例题:
首先,我们要知道n*n规模的矩阵,里面的子矩阵的数量是n4的规模。正方形子矩阵的数量是n3的规模。
那么这道题我们要怎么下手呢?
我们首先来一个粗暴的,那就是遍历行和列,二重循环确定左上角的点,然后遍历这个正方形的边长,注意边长的遍历上限。
好了,我们现在已经三重循环了,我们还要验证这个正方形的四条边,是不是数值都为1.
那么我们现在就是嵌套三个循环之后优嵌套了一个穿行的四个循环。太逆天了。
所以,我们要把验证数据的时间省下来。
这么看我们肯定是需要预处理的吧,这个应该没有什么疑问了,我们首先需要做一个矩阵,统计这个节点往右看,有多少个连续的1,再做一个矩阵,统计这个节点往下看,有多少个连续的1。
这个矩阵怎么用就很好联想了吧。
就是我们根据边长,去取矩阵的数据,和边长去比,大于边长就可以了。
这样一来,我们就从O(N4)->O(N3)
最后再来一个题目
这种题目也不罕见,一般这种问题,统一一个思路,那就是用二进制来拼。
我们来想一下,1-5的函数,可不可以改成等概率的0-1随机?
可以,只要1,2,我就返回0,随机到3,4,我就返回1,随机到5,我就叫他再随机一次
懂了吗?只要我们能0-1等价,我们就可以通过二进制返回所有的数据,多出来的就叫他再去随机一次,重新做就行。
所以第二问就好办了,首先改0-1等概率发生器。然后看要求的范围改成0-n之后,看要几个二进制位。然后对应的0-1去随,然后超了重新来就行了。
至于最后一个,那就需要点技术了,我们叫它随机两次,00,11都不要,01,10我们叫它分别对应0,1,这两个的概率都是p(1-p),刚好等概率,打完收工。
下一道题(没想到吧)
就是递归的思路,左树的情况*右树的情况
再来一道题(这回真的最后一个了)
首先,我们要会判断这个括号字符串完整不完整。这个好办,就一个变量统计左括号-右括号,遍历一遍字符串就行,如果变量不曾<0且最最后变量等于0.
那么这个怎么办也简单,就是遍历一遍,如果变量要<0了,就加一个括号,最后变量>0了,加变量个括号即可