约瑟夫问题的描述: n个人围成一圈,第一个人从1开始报数,报m的将被杀掉,下一个人接着从1开始报。如此反复,最后剩下一个,求最后的幸存者的编号(编号从零开始)。
如果需要知道每一轮被杀掉的人的编号的话,最好的方法可能是使用链表。不过如果只需要知道最后幸存者的编号的话,链表法的时间复杂度是无法接受的,使用递推公式解决这题会快很多。
递推公式百度一下你就知道,但关键是这个递推公式是如何推导出来的呢?
我们的目的当然是希望能用m和n表示出最后的幸存者的编号,但不幸的是这很难直接办到。但是用m和n表示出第一个被杀掉的人的编号并不难。那么如何表示呢?请大家先思考一下,忍住不要往下看~
↓从此处开始一光年↓
如果你的答案是(m - 1) mod n的话,那么你是对的!这个答案应该不难理解。
(注:mod表示取余)
现在我们已经杀掉一个人了,那么就还剩n - 1个人。因为我们是在找递推关系,所以如果能找到n个人的情况与n - 1个人的情况之间的关系的话,递推公式就出来了。而现在刚好圆圈里剩下n - 1个人,就意味着我们接下来需要重点分析这剩下的n - 1个人。
根据第一个被杀掉的人的编号,我们可以用m和n表示出其他人的编号,如下图。
接下来让我们对每个人重新编号。我们把第二轮报数中第一个报数的人,也就是第一个被杀死的人后面的那个人,重新编号为0,后面的人依次为1,2,3....。
哈哈。接下来仔细一看,问题不就已经被转换成了: n - 1个人围成一圈,第一个人从1开始报数,报m的将被杀掉,下一个人接着从1开始报。如此反复,最后剩下一个,求最后的幸存者的编号(编号从零开始)。除了“n个人”变成了“n - 1个人"以外,其他条件完全没变。
另外,我们也不难观察出新编号和旧编号之间的关系。容易观察出,如果一个人的新编号是i的话,那么他的旧编号就是(m + i) mod n。
到了现在,我们已经可以很轻松地推导出递推公式了。我们定义这样一个函数f(x,y) : f(x,y)表示当约瑟夫问题中的n = x,m = y时幸存者的编号。回到上面n - 1个人的情况,显然,幸存者的新编号就是f(n - 1,m),那么根据我们刚刚发现的新编号与旧编号之间的关系,就可以得出幸存者的旧编号是(m + f(n - 1,m)) mod n,这个不正好就是我们所寻找的递推关系嘛?
就讲到这里吧。至于具体如何用这个递推公式解决约瑟夫问题,太简单了,就留给你们自己想a.a
原文地址:https://www.cnblogs.com/ZhouYiJoe/p/12147052.html