约瑟夫问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。
解法一:数组法
#include
#include
int main(void)
{
int *person,i,node,n,m;
scanf("%d%d",&n,&m);
person=(int*)malloc(sizeof(int)*(n+1));
for(i=1; i<n; i++)//初始化圈
{
person[i]=i+1;//i表示编号为i的人,person[i]的值表示编号为i的人的下一个人的编号
}
person[n]=1;//编号为n的下一个人的编号是1
node=1;
while(node != person[node])//如果某个人的下一个人不是自己,意味着人数超过1人
{
for(i=1; i<m-1; i++)//这个循环终止于被杀的人的前一个人
{
node=person[node];//下一个人的编号为node,node的值来自于前一个人的person[node]
}
printf("%d号被杀\n",person[node]);//输出被杀的人编号
person[node]=person[person[node]];//修改被杀的人的前一个人的person[node]为被杀的人的后一个人的编号
node=person[node];//这句话中的node是被杀的人后一个人
}
printf("幸存者为%d号",node);//输出最后幸存者的编号
return 0;
}
解法二:单向环形链表法
#include
#include
#define LEN sizeof(struct node)//定义struct node这个类型的长度
struct node
{
int num;
struct node *next;
};
struct node *create(int m)
{
struct node *head,*p1,*p2;
int i;
p1=p2=(struct node*)malloc(LEN);
head=p1;
head->num=1;
for(i=1,p1->num=1;i<m;i++)
{
p1=(struct node*)malloc(LEN);
p1->num=i+1;
p2->next=p1;
p2=p1;
}
p2->next=head;
return head;
}
struct node *findout(struct node *start,int n)
{
int i;
struct node *p;
i=n;
p=start;
for(i=1;i<n-1;i++)
p=p->next;
return p;
}
struct node *letout(struct node *last)
{
struct node *out,*next;
out=last->next;
last->next=out->next;
next=out->next;
free(out);
return next;
}
int main(void)
{
int m,n,i,king;
struct node *p1,*p2;
scanf("%d",&m);
scanf("%d",&n);
if(n==1)
{
king=m;
}
else
{
p1=p2=create(m);
for(i=1;i<m;i++)
{
p2=findout(p1,n);
p1=p2;
p2=letout(p1);
p1=p2;
}
king=p2->num;
free(p2);
}
printf("最后剩下的人的编号是:%d\n",king);
return 0;
}
解法三:数学解法
开始报数,不断重复。第一个出列的人的编号一定是(M-1)%n,当有人出列后,下一个位置的人又从0开始报数。即将出列一人后的数据重新组成了0到(N-2)共N-1个人的列表,继续求N-1个参与人员,按报数到M-1出列,求解最后一个出列者在圆形队列中的编号。
易得出递推公式:
F(1)=0
F(N)=[F(N-1)+M-1]%N (N>1)
用C语言代码实现如下:
#include
#include
int josephus(int n,int m);
int main(void)
{
int n,m,i,s=0;
printf("输入参与人数N和出列位置M的值:");
scanf("%d%d",&n,&m);
printf("最后出列的人初始位置是%d\n",josephus(n,m)+1);
return 0;
}
int josephus(int n,int m)//递归函数
{
if(n==1)
return 0;
else
return (josephus(n-1,m),m)%n;
}
又因使用递归函数会占计算机较多内存,当递归层次太深时可能导致程序不能运行,因此,可以将程序直接编写为以下的递推形式:
#include
int main(void)
{
int n,m,i,s=0;
printf("输入参与人数N和出列位置M的值:");
scanf("%d%d",&n,&m);
for(i=2;i<=n;i++)
{
s=(s+m)%i;
}
printf("最后出列的人的初始位置是:%d",s+1);
}
两段代码输出结果相同。
水平有限,如有不足请指出。