约瑟夫问题两种解法

题目描述

有编号从1到N的N个小朋友在玩一种出圈的游戏。开始时N个小朋友围成一圈,编号为I+1的小朋友站在编号为I小朋友左边。编号为1的小朋友站在编号为N的小朋友左边。首先编号为1的小朋友开始报数,接着站在左边的小朋友顺序报数,直到数到某个数字M时就出圈。直到只剩下1个小朋友,则游戏完毕。

现在给定N,M,求N个小朋友的出圈顺序。

简单来说,就是每次删除一个人,输出,继续循环一直输出到只剩最后一个人,输出。

如果要输出最后一个人,前面的输出直接省去就可以了

#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个将被杀掉,最后剩下一个,其余人都将被杀掉。请写一个程序,求出最后会剩下的人的编号。

这个问题你们看到了数据规模很大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;
}

   小小见解,大神多多指教

posted @ 2019-07-30 12:04  fengwu2005  阅读(890)  评论(1编辑  收藏  举报