问题描述
据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,(39) 个犹太人与Josephus及他的朋友躲到一个洞中,(39)个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,(41)个人排成一个圆圈,由第(1)个人开始报数,每报数到第(3)人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过(k-2)个人(因为第一个人已经被越过),并杀掉第(k)个人。接着,再越过(k-1)个人,并杀掉第(k)个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决。Josephus要他的朋友先假装遵从,他将朋友与自己安排在第(16)个与第(31)个位置,于是逃过了这场死亡游戏。[1]
简单描述:(0,1,cdots,n-1) 这(n)个数字排成一个圆圈,从数字(0)开始,每次从这个圆圈里删除第(m)个数字,并将起点标记为第(m+1)个数字,求出这个圆圈里剩下的最后一个数字。
例如:
输入:n = 5, m = 3
对应数字为:0 1 2 3 4,删除的前4个数字依次为:2 0 4 1,最终剩下的数字为3。
解决方法
问题1:(0,1,cdots,n-1),删除第(m)个数字(第(m)个数字的序号为((m-1)))后,其余数字的序号是如何改变的?
若原序列某一位的序号为(k),删除第(m)个数字后,该数字的序号变为(k')。
若(k>m-1)(该数字在被删除数字之后),则(k'=k-m)
若(k<m-1)(该数字在被删除数字之前),则(k'=n-m+k)
综合考虑,有:(k'=(k-m)\%n),(\%)表示求余。
问题2:已知删除第(m)个数字后,某数字的序号为(k'),此序列中剩余数字的个数为(n-1),则删除第(m)个数字前,该数字的序号是?
根据问题1,我们已经得到了由(k)求(k')的关系式,那么由(k')求(k)的关系式可如下计算:
因为:
则:
有:
又已知:(k in [0, n-1])
则:
从而我们推出了由(k')求(k)的关系式:
问题3:说这么多废话,约瑟夫问题到底怎么解决?
对于最后的胜利者,也就是最终剩下的那个数字,他在这一轮的序号一定是(0)。
根据问题2中的阐述,我们可以推导出:这个胜利数字,在倒数第二轮中,他的序号是多少。(((0+m)\% 2))
依次类推,得到如下递关系式:
代码实现
具体题目描述见 LeetCode 剑指 Offer 62
class Solution {
public:
int lastRemaining(int n, int m) {
int count = 1;
int prev = 0;
int curr;
while(count <= n) {
curr = (prev + m) % count;
prev = curr;
count++;
}
return curr;
}
};
Ronald L.Graham,Donald E.Knuth,Oren Patashnik .具体数学计算机基础(第2版) :人民邮电出版社,2013:7 ↩︎