约瑟夫环问题学习小记
2017-10-26 08:03
106 查看
问题的提出
有n个人围成一个圈,按顺时针编号为1到n。现在从编号为1的人开始报1,下一个人报2,如此类推,报到m的人就退出,接着下一个人继续从1开始报。问最后一个剩下的人是谁。解法1
可以通过链表的形式来模拟这个过程,时间复杂度为O(nm)。解法2
我们把每个人的编号都-1,也就是编号变为了从0到n-1,显然这两个问题是等价的。第一个出去的人的编号肯定是(m−1)modn,然后接着从k=mmodn开始报。
那么剩下的人就是k,k+1,...,n−1,0,...,k−3,k−2。
考虑对这些人的编号做一下变换:
k变为0
k+1变为1
…
n−1变为n−k−1
0变为n−k
…
k−2变为n−2
k−1变为n−1
不难发现其实就是把i变为i−k。
那么当我们把第k−1个人去掉后,剩下的游戏就等价于一个大小为n−1的约瑟夫环。设其最后答案为x′,大小为n的约瑟夫环的答案为x,不难发现有x=(x′+m)。
通过这种思路不断地递归下去不难发现有如下递推式:
设f[n]表示大小为n的约瑟夫环每次走m步的最终答案。
f[1]=0
f[n]=(f[n−1]+m)modn
至此我们得到了一个比算法1要优秀的O(n)解法。
小拓展
当n很大而m很小的时候,比如说n<=109,m<=106,这时就有一些比较特殊的解法。当模数逐渐变大的时候,我们发现可能一次可以跳很多步且在这期间实际并不用取模,这时就可以一次性把这些位置都处理掉,从而大大的节约时间。
设当前模数为i,每一步跳k个位置,我们一次性跳t步。
不难得到w+t∗k<i+t−1化简后有t<i−w−1k−1。
那就是说这t步我们是可以一次性处理掉的。
代码
int solve(int n,int k) { int i=2,w=0; while (i<=n) { int t=(i-w-1)/(k-1); if ((i-w-1)%(k-1)==0) t--; if (i+t>n) {w+=(n-i+1)*k;break;} w+=t*k;i+=t; w=(w+k)%i;i++; } return w; }
在维基百科上面还有一种更优秀的解法:
至于如何证明,这个坑还是留着以后再填吧。据说 具体数学 里面有关于这条式子的证明。
相关文章推荐
- 算法学习之约瑟夫环问题
- 安卓学习中遇到的问题及经验小记
- 约瑟夫环学习小记
- 使用 Git 进行问题定位学习小记
- 【剑指Offer学习】【面试题45:圆圈中最后剩下的数字(约瑟夫环问题)】
- Qt入门学习小记——UDP收发解决汉字乱码问题
- 约瑟夫环加强版用线段树解决m,,这类问题还可以拓展,只是一个思路,用线段树的思路要学习
- 算法学习笔记之约瑟夫环问题
- 播布客教学视频_C学习笔记_10.1_约瑟夫环问题
- 黑马程序员--java实现约瑟夫环问题--java学习日记1(基础知识)
- 播布客教学视频_C学习笔记_10.2_约瑟夫环问题(数组链表)
- 【C#学习】利用数组解决约瑟夫环问题
- 倒水问题学习小记 Poj 1606 + 3414 + Hdu 1495 + UVA 10603
- Android学习小记-----拦截电话/拒接电话,规避拒接电话前响一声或者两声的问题
- 数据结构学习笔记(3)---循环链表(约瑟夫环问题)
- 2-sat 问题学习小记 Poj 3207 Ikki's Story IV - Panda's Trick (可行性判定)
- 回文串问题的克星——Palindrome Tree(回文树)/Palindrome Automaton(回文自动机)学习小记
- 华三笔试约瑟夫环问题
- 关于学习 spring-security 5.0.2 出现的问题
- 学习JavaScript遇到的this问题