约瑟夫环(Josephus)问题是由古罗马的史学家约瑟夫(Josephus)提出的,其表述方式有多种,较典型的一种是:
有n只猴子,按顺时针方向围成一圈选大王(编号从1到n),从第1号开始报数,一直数到m,数到m的猴子退出圈外,剩下的猴子再接着从1 开始报数。就这样,直到圈内只剩下一只猴子时,这个猴子就是猴王,编程求输入n,m后,输出最后猴王的编号。
- 解法一:数组实现
根据输入的n值确定数组的长度,对数组中的n个元素依次编号i+1(0≤i<n),然后这n个元素再进行从1到m的报数j(0≤j≤m),每当报到m时(m==j),将数组中对应元素的值置为0,并令j=0,总人数减1(cnt–);该元素后面的一个元素重新从1开始报数(j++)。(由于是数组实现,难以及时将置为0的空间删除,所以在报数前需要检验该元素是否为0(a[i]!=0?)。
当总人数(cnt)减为1时退出while()循环,输出数组中值不为0的元素,即最后留下的元素序号。
#include <iostream>
using namespace std;
int main()
{
int n,m,cnt;
cout<<"n=";
cin>>n;
cout<<"m=";
cin>>m;
int a[n];
for(int i=0;i<n;i++)
a[i]=i+1;
cnt=n;
int j=0;
while(cnt>1)
{
for(int i=0;i<n;i++)
{
if(a[i]!=0)
j++;
if(j==m)
{
j=0;
a[i]=0;
cnt--;
}
}
}
for(int i=0;i<n;i++)
if(a[i]!=0)
cout<<a[i]<<endl;
return 0;
}
- 解法二:循环链表实现
创建一个由n个结点(含头结点)构成的循环链表,每个结点的数字域存放1~n的整型值。这n个结点进行1到m的报数,每当报到m时,删除对应的结点(delete temp),并使n–,直到n减为1,退出循环,输出剩下的这个结点的数字域的值,即为该结点的序号。
#include<iostream>
using namespace std;
typedef struct SNode
{
int data;
SNode *next;
}SLNode;
main()
{
int ListJoseph(int n,int m);
int n,m;
cout<<"n=";
cin>>n;
cout<<"m=";
cin>>m;
ListJoseph(n,m);
return 0;
}
int ListJoseph(int n,int m)
{
SLNode head,*p,*q;
head.data=1; //head为头结点,其数据域存放第一个元素的值,而非数据域为空的头指针
p=&head;
int j=2;
if(n<2)
{
cout<<"总人数过少"<<endl;
return 0;
}
while(j<=n)
{
/*在链表末尾新增一个结点,并使新增结点的next指针指向头结点*/
q=new SLNode;
q->data=j;
q->next=&head;
p->next=q;
p=q;
j++;
}
p=&head;
int key=1;
while(n>1)
{
key++;
if(key==m)
{
/*当key==m时,删除对应的结点(p->next)*/
SLNode *temp;
temp=p->next;
delete temp;
p->next=p->next->next;
n--;
key=1;
}
p=p->next;
}
cout<<p->data<<endl;
return 1;
}
- 解法三:递归实现
在网上查到了用递归解决约瑟夫问题的代码(https://blog.csdn.net/m15682532244/article/details/78304165),代码很简短,但花了好几个小时仍不能完全理解,似乎理解了个大概,但自己无法说清具体是怎么回事。
我炮制的代码如下:
#include <iostream>
using namespace std;
int main()
{
int Joseph(int n,int m);
int n,m;
cout<<"n=";
cin>>n;
cout<<"m=";
cin>>m;
cout<<Joseph(n,m)<<endl;
return 0;
}
int Joseph(int n,int m)
{
if(n==2)
return((m%n)+1);
else
return((Joseph(n-1,m)-1+m)%n+1);
}
我令m=3,对2至11间的n值进行验证,代码是正确的,但其中的道理,是我所不能言说的,只将验证过程附在下面:
(第一行的值是1,2,3的循环,表示下面每一行的报数情况。下面每行最后标红的数字,是最后留下的数字。)