我们回到解题思路上,考虑一般的情况,n个人排成一圈,从第1个人开始报数,报数口号从1到m(报到m的人从圆圈里出去),循环直到只剩一个人,问最后剩下的人开始的编号是多少?
假设答案ans = F(n,m)。则F(1,m)=1; F(2,2) = 1; F(2,3) = 2;
假设有n个数: 1,2,3,4,5,6....,n-2,n-1,n。
中奖的第一个人的序号一定是:a1 = (m%n ).那么剩下的人构成一个n-1约瑟夫环:
1,2,3...a1-1,a1+1...n-1,n.
即: a1+1,a1+2....1,2,...a1-1. 每个元素做映射:
a1+1 -> 1,
a1+2 -> 2,
.....
a1-1 -> n-1.
剩下的数构成一个新的约瑟夫环并且规模为n-1.
因为从n转换到n-1的时候,每个元素都进行了减少a1的循环映射,而且a1 =m%n
所以有递推公式:F(n,m) = ( F(n-1,m) + (m%n) -1 )%n + 1.
且F(1,m)=1;
考虑更一般的情况:起始报数从第k个人开始:
则先将每个元素进行减少k-1的循环映射:
k -> 1
k+1 -> 2
....
k-1 -> n
则F(n,m,k) = (F(n,m) + k-1 -1) % n +1.
有了这些公式后就可以编程实现啦:
输入三个数,总数n,报数口号m,从第k个人开始报数。
输出是最后剩下的人开始的编号(注: 编号从1开始,报数从1开始)
- int Func(int n,int m,int k)
- {
- assert(n>0 && m>1 && k>0); //断言输入在正确的范围内
- if (n == 1) return 1;
- int ans = 1;
- int index = 1;
- while (index < n)
- {
- index++;
- ans = (ans + m%index -1) % index + 1;
- }
- ans = (ans + k-1 -1)%n + 1;
- return ans;
- }
时间复杂度是O(n).
451

被折叠的 条评论
为什么被折叠?



