题目:每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)。
这道题逻辑很清晰, 难点就在于每次小朋友出列以后报数的序列和小朋友的编号相关关系就被打乱了。 打乱就会导致不好分析。 所以要解决这道题,就要想办法对这两个序列的关系进行矫正。
现在编号是0~n-1 ,下面以a b c d 代表每次序列变换后的标识, 设k为出列序号的下一个序号。 k = m % n
a: 0, 1, ..., k- 1, k, k + 1, ..., n-1
首先出列一个小朋友,题目说的编号是m-1,考虑m可能会大于n,所以它的真实编号(m - 1) % n,
b: 0, 1, .., , k-2, k, k + 1, ..., n - 1
再对b序列做一个延长,
c1: 0, 1, ..., k-2, k, k+1, ..., n-1, n, n+1, ..., n + k - 2
取c1的k以后的序列为
c2: k, k+1,...,n-1,n,n+1,...,n+k-2.
再对c2序列减去一个k
d:0,1,...,n-2.
通过这样的操作我们将序号和编号进行了矫正。其实就是出列以后下一个数字变成了起始序号, 怎么把他们转换成起始序号的问题。(原先是0~n-1, 出列一个小朋友后变成了0~n-2, 从而让这个问题可以被递归分解进行解决)。 现在唯一的问题就是要用逻辑代码对每次出列后的序列都实现这个矫正过程。
假设d是x
d->c2 : 就是对序号进行加上一个k值就可以了 , c2 = x + k;
c2->b : 这里直接看c2->b也是一样的, c2多了n, n + 1, ..., n + k - 2 ,而b多了0,1,...,k-2.所以 c2 %n = (x + k ) % n = b.
所以总结下来序列的反推公式为 (d + k) % n = b
知道了序列的反推调整后还不够,现在要根据序列的推导来得知序列结果的推导 前提条件如下
(1)序列结果也就是当圈圈大小为1时 f[1] = 0是确定的
(2)知道了序列就知道了它的出列结果。
(3) 序列推导公式(d + k ) % n = b
序列推导怎么关联上序列结果的推导呢?是这样的首先这个环最终尺寸是1,唯一的下标0就是胜出者。假设 m = 4,这个环每增加1个人进行调整。心机的小明在以这个结果为前提下每次调整后都根据这个序列推导公式去占有相应的序号。两个人的时候小明回忆了下序列的推导公式,发现(0 + 2) % 2 = 0; 他在两个人的时候就不动, 这样子在报数的时候下标序号为1的人先出列, 然后小明就赢了, 在三个人的时候又根据这个公式求(0 + 1) % 3 = 1 去占据序号1. 就这样不停的去增加就永远不会输了。所以实际上序列推导公式就是序列的结果推导公式
f[n] = (f[n-1] + k) % n