问题描述:编号为1~n共n只猴子坐成一圈想要选大王,大家约定从1开始报数数到n的猴子退出,剩下的猴子继续从1开始报数,直到剩下最后一只猴子时,它就被选为大王。
有一种常规的解决思路:模拟选大王的过程。用一个长度为n数组作为标志数组,将每次退出猴子对应编号上标记为FALSE,继续报数,遇到标记为false的元素跳过继续,直到数组中只剩一个标记为true的数组,其就是大王。时间复杂度O(mn)。
为了感性认识,请大家考虑一种极端情况,如果n为100只,最后只剩下2只标记为true的猴子,我们为了得到大王,本只需在2只猴子间进行报数,谁最先报出m,则淘汰。但是程序的做法却需要将100个猴子扫描m-1次进行m-1计数,直到报出m。是不是很复杂?
为了得到大王,我们得拿出我们数学这把利剑,采用递推方式,只需要时间复杂度为O(n)就能解决问题。下面细细道来
递推方法:递推,顾名思义从猴子少的时候推到猴子多的时候,找其中的规律。因为习惯我们把编号设成0~n-1,对于上面问题求解时,我们再将编号加一就是了。以下取m=3进行说明。
f(i):表示当猴子数量为i时,猴王的编号。例如:f(1)=0,当只有一只猴子时,其编号为0,它肯定就是猴王了。f(2)=1,两只猴子从编号为0开始报数,报数为3的猴子退出,很明显猴王编号为1。下面给出猴子依次变化时,猴王变化情况:
发现规律了f(i)=(f(i-1)+m)%i.对这就是我们需要的。那么要求猴子总数为n的时候,已知初始值f(1)=0,又有了递推公式,是不是很简单了。
再拓展下,我们能不能得到n只猴子依次出队的序列呢,如果这个知道,那儿我们就可以很容易算出编号为i的猴子是第几次出队的,是吧?
咱们继续找规律吧。f(i)下面对应了一列数,从下往上,表示当猴子总数为i时,依次出队的先后顺序。
是不是发现了?每个标蓝色的数字其对应的一行数字之间仍然是满足(f(j)+m)%i【采用(j)只是为了与猴王的f(i)避免冲突】,但是我们不知道递归的初始值啊,即:蓝色数字怎么求呢?有以下两个方案:
1)对于f(i)序列中共有0,1··i-1个数,我们如果已经知道除了蓝色数以外的数字,通过搜索排除就能知道蓝色数
2)针对每个f(i)序列和我们能算出,用其减去已递推了的数,就能知道蓝色数了
时间复杂度为O(n*n).
代码已传到优快云,希望大家多多批评指正。