最近被问到的一些题目记录一下

题目一:

从0到2∧32-1,去掉其中某一个数,要求用最少的内存空间找到那个数

思路:

看到这个题目,第一个关注点就是0-2∧32-1。会想到用二进制来表示这些数据,而这2∧32个数,用二进制表示的时候,每一位出现的1和0次数都是一样的。如果少了其中的某一个数,每一位就会少一个0或者一个1。
基于此,我们可以对每一位统计0和1的个数。0少那一位就是说明删除的数该位为0,1少那一位就说明删除的数该位为1.通过这种方式,找到去掉数的二进制表示方式。
这样只需要32位额外的内存空间做辅助,来找到去掉的数。

题目二:斐波那契博弈(Fibonacci Nim)

两个人A和B,先后从一堆棋子里拿棋子,A先手。有以下规则:

  1. A第一次不能拿走全部
  2. 后拿的一个人拿走棋子的数目不能超过上一次对方拿走棋子数目的2倍
  3. 至少拿走1个棋子
  4. 谁拿走最后一枚棋子,谁就赢
    问,在什么情况下,A必败

结论:

当棋子数为Fibonacci数的时候,A必败
即设棋子数为n,n=2.3.5.8.13…时,A必败

证明:

用数学归纳法可证明:
证明过程可参考这个

当n不是Fibonacci数的时候
需要借助“Zeckendorf定理”(齐肯多夫定理):任何正整数可以表示为若干个不连续的Fibonacci数之和。
定理证明这里略过。

分解的时候,要取尽量大的Fibonacci数。
比如分解40:40在34和55之间,于是可以写成40=34+6,然后继续分解6,6在5和8之间,所以可以写成6=5+1,
最后分解成40=34+5+1。

假如n=83 = 55+21+5+2
假如先手取2颗,那么后手无法取5颗或更多,而5是一个Fibonacci数,那么一定是先手取走这5颗石子中的最后一颗,同理,接下去先手取走接下来的后21颗中的最后一颗,再取走后55颗中的最后一颗,即对于以后的每一堆,先手都可以取到这一堆的最后一颗石子,从而获得游戏的胜利。

题目三

给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。

例如,给定 n = 2,返回1(2 = 1 + 1);给定 n = 10,返回36(10 = 3 + 3 + 4)。

思路:

有穷遍历显然不合适,先尝试列举,看有没有规律发现。
按照题目拆分为至少两个正整数的要求,
2只能拆分为1和1,返回1
3可以拆分为1和2,返回2
4可以拆分为2和2,返回4
5可以拆分为2和3,返回6
6可以拆分为3和3,返回9
7可以拆分为2、2、3,返回12
到这里规律似乎明显了,所有的数最终都可以拆分出若干个2和3,2是最小的偶质数,3是最小的奇质数。
6可以拆分为3、3,也可以拆分成2、2、2,显然3、3的乘积更大。
总结一下规律:
如果正整数n小于3,则返回n-1,如果n>=3,则将n拆分为若干个2和3,且优先拆分出3。
按照优先拆分出3的原则,可以使用一个递归完成:
从数字n大于3,从中拆分出一个3,再将n-3重复这个过程,直到n-3小于等于3。
又因为4可以拆为2、2,4=2*2,所以若n-3为4,也可以停止递归。
代码实现如下:

    public static int integerBreak(int n) {
        if (n <= 3) {
            return n - 1;
        }
        if (n == 4) {
            return 4;
        }
        // 若n-3<=3,停止递归,又因为4可以拆为2、2,4=2*2,所以若n-3为4,也可以停止递归。
        if (n - 3 <= 4) {
            return 3 * (n - 3);
        }
        return 3 * integerBreak(n - 3);
    }
发布了18 篇原创文章 · 获赞 1 · 访问量 2311

猜你喜欢

转载自blog.csdn.net/Better_WZQ/article/details/104156046
今日推荐