最近在@研究者July 的微博上看到一道有趣的逻辑题,是阿里巴巴2016年校招笔试题,原微博地址如下:http://weibo.com/1580904460/CyEfEyOT6?type=comment#_rnd1441345987133 。顺便说一下,读书的时候@研究者July的博客我也是经常看的,学到不少东西。在这里把这道题搬过来,并给出自己的解法。
是一道选择题,原题如下:
1, 2, 3, …… , 49, 50里选择一个集合S,使得若x属于S,则2x不属于S,则S最多能有()个元素。
A. 25 B. 27 C. 30 D. 33 E. 36 F. 37
这道题如果直接暴力求解也可以很快得到答案,本文想给出一个一般化的解法。
假设最大的数不是50,而是n。根据题意,我们只需将1到n之间所有有2倍关系的数连接起来构造一个“二倍链”,因为不同的二倍链之间不会相互干扰,所以可以单独考虑每个二倍链最多能取多少个数,然后将所有二倍链可取的数目加起来即可。
比如n=11,那么所有的二倍链有:
1--2--4--8
3--6
5--10
7
9
11
我们发现,每个二倍链的第一个数一定是奇数;如果是偶数,因为偶数一定是某个数的二倍,所以一定已经在前面的二倍链中出现过。另外,如果从一个二倍链中取某一个数,那么这个数左右两边的数(如果有)就不能再取了,即满足“相邻互斥性”。
接着,我们要计算每个二倍链最多能拿出多少个数构成集合S。这里先给出结论,对于长度为len的链,最多可以取个数构成集合S。当然这个结论需要严格的证明,证明过程也不难,这里做简化说明。
当len为偶数时,只需要在二倍链中间隔取数即可,所以共可取len/2个数。当len为奇数时,要从第一个数开始间隔取数,这样首尾两个数都能取到,所以最多可以取(len + 1) / 2 个数。例如,对于二倍链1--2--4--8,既可以取{1, 4},也可以取{2, 8};而对于1--2--4--8--16,只能取{1, 4, 16}是最多的。所以问题转化成求每个二倍链的长度。
更进一步,其实每个二倍链是这样的结构(不妨设n为奇数):
PS:以上最后一个二倍链只有n本身。
所以每个以奇数c开始的二倍链的长度为,其中为
故S中元素的最大个数为
同理,当n为偶数时,S中元素的最大个数为
最后再废话一下,上面两式可以合并为一个式子表示。假设m为小于n的最大奇数,那么
因此(1)(2)两式可以合并写为
最后,当n = 50时,带入(4)式,用matlab命令
>> sum(floor(log2(n ./ [1 : 2 : n - 1]) / 2)) + n / 2
容易求出结果为33,因此答案为D。