约瑟夫环--“丢手绢问题”

约瑟夫问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。

解法一:数组法

#include<stdio.h>
#include<malloc.h>
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 <stdio.h>
#include <malloc.h>
#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<stdio.h>
#include<string.h>

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<stdio.h>
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); 
}

两段代码输出结果相同。
水平有限,如有不足请指出。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值