1.什么是约瑟夫问题
设有编号为1,2,……,n的n(n>0)个人围成一个圈,从第1个人开始报数,报到m时停止报数,报m的人出圈,再从他的下一个人起重新报数,报到m时停止报数,报m的出圈,……,如此下去,直到所有人全部出圈为止。当任意给定n和m后,设计算法求n个人出圈的次序。
2.暴力解法
最直接的方法就是模拟报数的过程。模拟n-1次报数的过程,每次报数报m个人,第m个人被淘汰。最后查看哪个编号没有报到,该编号为结果。# 学习目标:
但是这样的时间复杂度是O(m*n)。所以当m和n较大的时候,程序的运行时间将特别长。
3.递推公式解法
步骤一:定义f(n)
将n个人报数,报m的人出圈的问题视为f(n,m)问题,并且设该问题的解为f(n)。那么n-1个人报数,报m的人出圈的问题视为f(n-1,m)问题,并且设该问题的解为f(n-1)。
f(n,m) | n个人报数,报m的人出圈 |
---|---|
f(n-1,m) | n-1个人报数,报m的人出圈 |
… | … |
f(1,m) | 1个人报数,报m的人出圈 |
步骤二 找到f(n)与f(n-1)之间的关系
假设一开始所有人的序号为:
1 2 3 … n-1 n
那么第一次报数之后,只剩下如下的人:
1 2 3 … m-1 m+1 … n-1 n
并且下一次报数之后,从m+1开始数起,于是序号应该变为:
m+1 m+2 …n-1 n 1 2 3 … m-1
(设这里的序号为x)
将上述序号对应到下列序号:
1 2 3 … n-2 n-1
(设这里的序号为y)
则x和y的关系有:
x=(y+m)%n
其中,这里的序号x代表的是f(n,m)中的序号,序号y代表的是f(n-1,m)中的序号。那么f(n)与f(n-1)的关系有
f(n) =( f(n-1)+m )%n
f(n-1)=( f(n-2)+m )%(n-1)
f(n-2)=( f(n-3)+m )%(n-2)
…
f(2)=( f(1)+m )%2
f(1)=1
f(1)=1是因为只有一个人,那肯定它就是最后剩下的人,序号为1。
步骤三 根据递推公式写代码
根据递推公式,f(1)=1之后,那么就可以从下往上得到答案。先求得f(2).再求f(3)直到f(n).所以有如下代码。
int yuesefu(int n, int m) {
int x = 1;
for (int i = 2; i <= n; i++) {
x = (x + m) % i;
}
return x;
}