详解约瑟夫环问题
Posted rane
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了详解约瑟夫环问题相关的知识,希望对你有一定的参考价值。
约瑟夫问题:n个人围坐成一圈,从1开始顺序编号;游戏开始,从第一个人开始由1到m循环报数,
报到m的人退出圈外,问最后剩下的那个人原来的序号。
问题分析:面对这样循环报数的数据,我们最容易想到的就是用数组进行报数的模拟,最后把存活的人的编号输出。
先贴上这种思路的代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<vector> 5 #include<algorithm> // find() 函数 6 7 int main(){ 8 int m,n; 9 cin>>n>>m; 10 vector<bool> s(n, 1); 11 int dead(0), cnt(0); 12 auto it=s.begin(); 13 while(dead < n-1){ 14 if(it == s.end()) it = s.begin(); 15 if(*it) cnt++; 16 if(cnt == m){ 17 *it = 0; 18 cnt = 0; 19 dead++; 20 } 21 it++; 22 }23 it=find(s.begin(), s.end(), 1); 24 cout<<it-s.begin()+1; 25 return 0; 26 }
代码思路是采用 vector<bool> 容器来存储每一个人的存在状态,如果在圈内参与报数,则值为1,退出圈后为0 。dead 是退出圈不参与报数的人数,作为报数停止的条件,即当 dead == n-1 时,报数停止。cnt 是每一个人报的数,当 cnt == m 时,报该数的人退出圈不再参与报数,在vector数组中的位置变为 0。
第十行,使用vector的迭代器来表示报数成员所对应的下标,while循环中即报数的模拟过程。
第十四行,因为迭代器运行过程中,可能指向 s.end() ,这一位置并不在vector 内,且没有有意义的数据,于是把迭代器重新指向 s.begin() ,这一过程解决了环形报数中第一个人和最后一个人连接的问题。
第十五行,表示报数过程,vector 数组中,此成员参与报数的话,cnt++ ,模拟报数的进行。
第十六行开始,遇到报到 m 的人,vector 中所对应的数据变为0,dead++ ,表示不再参与报数。cnt 置 0 ,表示所报数字重新从1开始(为什么不让cnt=0:因为下一个人出现时,才会自增1)。
第二十三行,得到最后那个人的迭代器指向。由于 vector 迭代器是随机访问迭代器,支持加减运算,所以刚才得到的迭代器指向减去 s.begin() ,就是最后留下的人所对应的下标(为什么不是他的原序号:因为下标从 0 开始计数),该结果再加一就是最后留下的人的原序号。
模拟的效率似乎不是很高,而且代码量也不算少,那有什么简单的办法呢?
先贴代码:
递归调用:
#include<iostream> #include<cstdio> int joseph(int n, int m){ return n==1 ? 0 : (joseph(n-1, m)+m)%n; } int main(){ int m,n; cin>>n>>m; cout<<joseph(n, m) +1; return 0; }
待续...
以上是关于详解约瑟夫环问题的主要内容,如果未能解决你的问题,请参考以下文章