约瑟夫问题两种解法
Posted fengwu2005
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了约瑟夫问题两种解法相关的知识,希望对你有一定的参考价值。
题目描述
有编号从1到N的N个小朋友在玩一种出圈的游戏。开始时N个小朋友围成一圈,编号为I+1的小朋友站在编号为I小朋友左边。编号为1的小朋友站在编号为N的小朋友左边。首先编号为1的小朋友开始报数,接着站在左边的小朋友顺序报数,直到数到某个数字M时就出圈。直到只剩下1个小朋友,则游戏完毕。
现在给定N,M,求N个小朋友的出圈顺序。
输入格式
唯一的一行包含两个整数N,M。(1<=N,M<=30000)
输出格式
唯一的一行包含N个整数,每两个整数中间用空格隔开,第I个整数表示第I个出圈的小朋友的编号
样例
样例输入
5 3
样例输出
3 1 5 2 4
简单来说,就是每次删除一个人,输出,继续循环一直输出到只剩最后一个人,输出。
如果要输出最后一个人,前面的输出直接省去就可以了
#include<iostream> #include<cstdio> using namespace std; int main() bool m[30001];//存每个人是否还活着,false活着,true死了 int a,b;//a个人,b出圈 cin>>a>>b; for(int i=1;i<=a;++i) m[i]=false;//全部活着 int c=0,d=0,e=0;//d为现在报的数 do ++c; if(c==a+1)//如果到最后一个人后面,回到第一个,模拟环状 c=1; if(m[c]==false)//如果这个人活着,报数 ++d; if(d==b) d=0;//归零 cout<<c<<" ";//要是只求最后一个人,省去 m[c]=true;//这个人死了。。。。 ++e; while(e!=a);//如果都死了,跳出
上面这个是简单版,时间复杂度为O(n2)
下面这个很烧脑,准备接住
约瑟夫问题2解决
题目描述
约瑟夫问题是个有名的问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。请写一个程序,求出最后会剩下的人的编号。
输入格式
输入只有一行,为两个整数n,m(0<n,m<10^8)。
输出格式
只有一行,一个数,为剩下最后一个人的编号
样例
样例输入
6 5
样例输出
1
这个问题你们看到了数据规模很大n2绝对超时
当N的值有上百万,M的值为几万时,到最后虽然只剩2个人,也需要循环几万次(M的数量)才能确定2个人中下一个出列的序号。显然,在这个程序的执行过程中,很多步骤都是进行重复无用的循环。现在有一个强大的数学规律
解析:
其中,在约瑟夫环中,只是需要求出最后的一个出列者最初的序号,而不必要去模拟整个报数的过程。因此,为了追求效率,可以考虑从数学角度进行推算,找出规律然后再编写程序即可。
我们从0开始循环(从零开始报数),到n-1,共n个人,第一个出圈的人是报数m-1的,所以他的编号是m-1或(m-1)%n(因为是从零开始,所以不用考虑+1问题)
现在有n-1个人,从0循环到n-2(已经出圈的人的空位删掉),报数现在(删掉空位后)从(m-1)%n报0,1,2,3,4...。(m-1+(m-1)%n
)%(n-1)出圈,每一次都模现在的人数。
实际上就是一层层的模
现在我们逆推,假设现在只剩下1人,出圈的肯定是0号。
设f=0,f是最后活的那个人在只剩下一个人的环中的编号
现在进入两个人的环,报m-1的出圈,他就是当时的第m个,所以他在两个人的圈中的编号就是f+m,由于人数可能少于m,所以要用模,f=(f+m)%n n=2;
然后进入三个人的圈,m-1继续出圈,他是第m个,在上一圈里,他是f,所以f=(f+m)%n n=3;
以此类推,n=4,5,6,7,8......
因为题中是从1开始报,所以在算出n个人的圈中,那个人的编号f后,要+1;
程序很简单,一个循环就搞定;
#include<iostream> using namespace std; long long n,m,f;//在外面自动赋值为零 int main() cin>>n>>m; for(int i=2;i<=n;i++)//用i计算人数 f=(f+m)%i; f++; cout<<f;
小小见解,大神多多指教
以上是关于约瑟夫问题两种解法的主要内容,如果未能解决你的问题,请参考以下文章