约瑟夫环(Josephus)问题:
已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
例如:n = 9, k = 1, m = 5
【解答】
出局人的顺序为5, 1, 7, 4, 3, 6, 9, 2, 8。
解法一:用数组模拟
#include<iostream>
using namespace std;
int main()
{
int sign,n,m,i,Loop[100],Count;
while(cin>>n>>m&&n&&m)
{
for(i=0;i<n;i++)
Loop[i]=i+1;
sign=0;
for(i=0;i<n;i++)
{
Count=0;
while(Count<m)
{
while(Loop[sign]==0)//越过退出的位置
sign=(sign+1)%n;
sign=(sign+1)%n;
Count++;
}
sign--;//实际标号的
if(sign<0)
sign=n-1;
if(i==n-1)
cout<<Loop[sign]<<endl;
Loop[sign]=0;
}
}
return 0;
}
解法二:
//从位置考虑,举例说:
nNum:5 move:2
1 2 3 4 5
3 4 5 1
5 1 3
3 5
3
3在最后一次的pos1位置是0;
倒推,它的位置pos2为(0+2)%2=0;
继续,它的位置pos3为(0+2)%3=2;
pos4=(pos3+2)%4=0;
pos5=(pos4+2)%5=2;
因为实际生活中编号总是从1开始,所以输出加一。
# include<stdio.h>
int main()
{
int nNum,move,i,pos;
scanf("%d %d",&nNum,&move);
pos=0;
for(i=2;i<=nNum;i++)//倒推
pos=(pos+move)%i;//把pos看成最后那个人所处的位置
printf("%d\n",pos+1);
return 0;
}
//也可递归:
令f[i]表示i个人报m退出最后胜利者的编号,那最后的结果是f[n].
递推公式:
f[1]=0;
f[i]=(f[i-1]+m)%i; (i>1)
解法三:
//刚看了看链表,就用链表模拟一下
# include<stdio.h>
# include<malloc.h>
# define L sizeof(struct node)
typedef struct node
{
int num;
struct node *next;
}SeqList;
SeqList *head,*tail;
void create(int n)//创建链表
{
SeqList *p;
int i;
for(i=1;i<=n;i++)
{
p=(SeqList *)malloc(L);
p->num=i;
p->next=NULL;
if(i==1)
{
head=p;
tail=p;
continue;
}
tail->next=p;
tail=p;
}
tail->next=head;
}
void DeletList(int m)//查找报m的人,并删除该节点
{
SeqList *p,*q;
int count=0;
p=tail;
do
{
q=p->next;
count++;
if(count%m==0)
{
p->next=q->next;
free(q);
}
else p=q;
}while(p!=p->next);
head=p;
}
int main()
{
int n,m;
while(scanf("%d %d",&n,&m)!=EOF)//n个人,数到m出列
{
if(n==1)
printf("1\n");
else
{
create(n);
DeletList(m);
printf("%d\n",head->num);
}
}
return 0;
}