zoukankan      html  css  js  c++  java
  • 《编程之美》笔记


    第1章 游戏之乐----游戏中碰到的题目

    第2章 数学之魅----数字中的技巧

     2.2 不要被阶乘吓倒

      问题2:求N!的二进制表示中最低位1的位置。

      方法一:一个数字k如果乘以2,则其二进制就会左移一位。那么N!的质因子分解中的2的个数就是N!的二进制中有多少个后缀零。具体实现与N!后缀零一样。

     1 int lowestOne(int N)
     2 {
     3     int ans=0;
     4     while(N)
     5     {
     6         N>>=1;
     7         ans+=N;
     8     }
     9     return ans+1;//由于求的是后缀0的个数,所以最后一个1应该是ans+1
    10 }
    实现

      方法二:答案为N!减去其二进制表示中含1的个数。比如 11011-1-1-1-1=(10000-1)+(1000-1)+(10-1)+(1-1)=1111+111+1=1101+110+11+1。需观察规律。

     2.3 寻找发帖水王

      扩展问题:一个大小为N的数组中有3个特殊的数,这3个数都出现了超过N的1/4次, 求这3个数。

      方法:同“寻找出现次数超过一半的数”一样,只不过现在的问题是3个。由于这3个数的出现次数之和超过了N的1/4,那么同样可以用3个桶来做抵消法。具体逻辑为,对于新考虑的数t,若t已经在桶中,则将t继续丢往该桶;若t不在桶中,且有空桶,则将t丢往任一空桶了;若t不在桶中,且无空桶,则随便将任一个数从任一桶中删除。最后留在3桶中的数字就是答案。

     2.4 1的数目

      问题1:写一个函数f(N),返回1到N之间出现的“1”的个数,比如f(12)=5。

      方法一:暴力统计,复杂度太高。

      方法二: 类似于数位DP,考虑十进制的第i位分别为0,1和其他的情况就可以解决。当然,数位DP也是可以做。

      2.5 寻找最大的K个数

      问题:寻找第K大的数(相当于寻找最大的K个数)。

      方法一:排序一遍即可解决。

      方法二:类似快排,递归不断地进行二分当前的数组。

      方法三:找到数组中的最大数big及最小的数small,对答案ans在[big,small]进行二分枚举,每次枚举需要扫一遍整个数组。

      方法四:当数组很大时,必须减少遍历数组的次数。若K比较小,可用堆维护当前最大的K个数,pop出一个最小的,加入一个更大的。仅需扫描一遍数组。

      方法五:若big-small比较大,可以对其进行分块,判断第K个数具体在哪个块,若还是很大,可以递归继续分快处理。

     2.9 斐波那契(Fibonacci)数列

      问题:有递推式F(n)=F(n-1)+F(n-2),且F(0)=0,F(1)=1。给定一个自然数N,求F(N)。

      方法一:递归求解。缺点是有重复计算,复杂度高。

      方法二:递推求解,时间复杂度O(n),空间O(1)。

      方法三:求通项公式,用特征根方程求解。缺点是结果为实数,当N较大时有精度问题。

      方法四:构造矩阵,用矩阵快速米求解。复杂度O(logn)。

      扩展问题:假设A[0]=1,A[1]=2,A[2]=2,对于任意n>=3有A[n]=A[n-1]+A[n-2]+A[n-3]。求A[n]?

      方法:构造矩阵法是一般的比较快的解法(当然也可以研究递推式找规律),对于这种递推,右式仅有3项已计算过,仅需要构造一个3*3的矩阵就可以解决。

      矩阵应该为:[0, 0 ,1]

            [1, 0, 1]

            [0, 1, 1]

      构造要点:矩阵中的某个格子代表右式中对应项的系数,设A[n]=a*A[n-1]+b*A[n-2]+c*A[n-3],则矩阵为:

                           [0, 0 ,a]

            [b, 0, b]

            [0, c, c]

      2.15 子数组之和的最大值(二维的最大子段和)

      问题:在一个n*m的矩阵中求最大的子矩阵和?

      方法一:枚举子矩阵的两个角,再求和。复杂度O(n2*m2*sum的时间复杂度)。为降低求sum的时间复杂度为O(1),预处理部分和。复杂度O(n2*m2)。

      方法二:降维思想,假设子矩阵在i~j行之间,则可以按一维的思想求这几列的最大子段和(需要预处理求和)。当然也可以枚举i~j列,复杂度O(n*m*min(n,m))。

      扩展问题1:如果矩阵也是首尾相接(行相接),怎么处理?

      方法一:假设不经过缺口,枚举i~j行,求列的最大子段和。假设经过缺口,矩阵取反再求一次。

     2.21 只考加法的面试题

      问题一:判断一个64bit的自然数n是否可能为一组连续的自然数之和(不少于2个),若是,输出所有的可能。

      方法:假设有解,设该解中有k个自然数,且最大的自然数为i,则有k*(i+i-k+1)/2=n,即k*(i+i-k+1)=2*n。那么k就是2*n的一个因子,则只需要测试sqrt(2*n)次就可以知道解了。

      问题二:在问题一中,哪些自然数是没有解的?

      方法:打表知1,2,4,8,16...2j都没有。证明一下,这些数都是2t,它们的因子也只有2j的因子,即除了1之外都是偶数。由k*(2*i-k+1)=2*n得知k和(2*i-k+1) 不能同时为偶数,所以这些数无解。

      问题三:在问题一中,某一解中的自然数最多的n是多少?

      方法:将16bit内的n都打个表,观察到只有从1+2+...+m=n时才是最多的,即m最大。那么就一直统计前缀和,只要pre_sum最大且小于64bit就是答案了。

    第3章 结构之法----字符串及链表的探索

      

    第4章 数学之趣----数学游戏的乐趣

      4.7 蚂蚁爬杆

      扩展问题4:两人A(速度a),B(速度b)一直在路上相向而行,在A和B之间的距离为s的时候,A放出了一只鸽子C(速度为c),C飞到B后,立即掉头,遇到A后又飞向B,一直往返地飞行,直到AB相遇,问这期间鸽子共飞了多少路程?

      方法:A和B会在t=s/(a+b) 单位时间后相遇,而鸽子一直没有停下来,那么鸽子飞了len=t * c的路程(这和鸽子的方向无关)。

       扩展问题5:轮船(速度为a)在长江(速度为b)里逆流而上行驶。某个时刻,从穿上落下一个救生圈到水中。一个小时候,船员才发现这一情况,于是掉头去找。问什么时候轮船可找到这个救生圈?

      方法:假设船为A,救生圈为B。一个小时后A和B的距离为len=60*b+60*(a-b)=60*a,设经过t分钟A追上B,且走了s路程,那么列出两条式子:(a+b)*t=s,b*t=s-len,其中只有2个变量,联合求解即可得到 t=60。

  • 相关阅读:
    Java并发之线程管理(线程基础知识)
    spring aop使用
    java动态代理
    java深拷贝与浅拷贝
    装饰模式(也叫包装模式)
    Spring基于XML方式的使用
    javaWeb域对象
    静态代理和动态代理
    getAnnotation的一个坑
    (转)文件上传org.apache.tomcat.util.http.fileupload.FileUploadException: Stream closed
  • 原文地址:https://www.cnblogs.com/xcw0754/p/4922943.html
Copyright © 2011-2022 走看看