百练-2746-OnlineJudge-约瑟夫问题.一(栈,数组模拟)

来源:互联网 发布:诺基亚运行python 编辑:程序博客网 时间:2024/05/17 02:49

#include<stdio.h>
typedef struct Node
{
 int data;
 Node *next;
 Node(int i){   //Node函数
  data=i;
  next=NULL;
 }
}node;
int main()
{
 int i,m,n;
 node *head,*p,*q;
 while(scanf("%d%d",&n,&m),m||n)
 {
  if(m==1)              //这点需要注意,m==1时,特殊情况
  {
   printf("%d\n",n);
   continue;
  }
  head=new Node(1);//初始化
  for(p=head,i=2;i<=n;i++)// 正序输入链表元素
  {
   q=new Node(i);//初始化
   q->next=p->next;
   p->next=q;
   p=q;
  }
  p->next=head;//构造循环链表,实现首尾相连
  for(p=head;p->next!=p;p=p->next)//删除m的元素
  {
   for(i=1;i<m-1;i++)
   p=p->next;
   q=p->next;
   p->next=q->next;
   delete q;
  }
  printf("%d\n",p->data);
 }
 return 0;
}
   
1.第一种方法:
分析:设一个包括m个元素的数组,初始值将数组的每个元素放1。从第一个元素开始,
依次取数组元素相加,当其和为n 时,输出该元素的下标(它即是应该出圈人的编号)。然
后将该元素清0,使以后相加时不再起作用,相当于该人已出圈。再从下一个元素开始,依
次取数组元素相加,当其和为n 时,再输出该元素的下标,如此继续,直到输出m 个值以
后结束。

2. 第二种方法
分析用循环链表解决问题,首先需要构造一个循环链表,构造循环链表的方法很简单,
只要将最后一个人的下一个指针指向第一个人,这样就构成一个环,如图所示,构造循环链
表以后,就可以进行删除操作,直到循环链表剩下一个人为止。

3.第三种方法
根据方法二的联想,可以通过数组元素的值是下一数组元素的下标构成环,再利用数组
下标还是数组把前一元素跳过该元素与再下一个元素相连,不再访问已出圈的元素,从而加
快速度。
设一个包括m个元素的数组,在每个数组元素中存放与其相连的下一个元素的编号。
当某人出圈时,将对应元素的值放入前一元素中,使得前一元素跳过该元素与再下一元素相
连,因而再也不会访问已出圈的元素了。以5 个人围成一圈为例,开始数组a 按如下形式存
放数据:
数组元素 A[1] A[2] A[3] A[4] A[5]
数据 2 3 4 5 1
当第3 个人出圈时,将元素a[3]的值4 送入a[2]中,使得元素a[2]直接和元素a[4]相连。

4.第四种方法
让我们进一步思考,能否利用数学知识,归纳递推公式。例如5 人按3 报数的情况,
可以得到出圈的先后序列:3,1,5,2,4,在这里最后一个出圈者编号为4,实际上第一
个出圈的人(编号3)出圈以后,把编号4和5 上移一个元素,可以看作是剩余的4 个人继
续按3 报数了,把剩余的4 人再按上述处理,这样最后一个出圈者编号在位置1 中。
推广到一般情况,即共有m个人从第s人开始报数,按n报数的情况,当剩余I个人的
时候,第s 编号的人出圈以后,对后面元素上移一个元素,归纳出圈者编号的递推公式:s(出
圈编号)=(s+n-1) mod I。举例:共有16 人,从第1 人开始报数,按8报数的情况,按上述递
推公式计算,第一个出圈者的编号为s=(1+8-1)mod 16得8,剩余15(即I=15)人,继续计
算第二个出圈者的编号为s=(8+8-1) mod 15 得0, 此时为特例,当s=0 时,出圈编号正好既
是报数的倍数又是末尾一个编号(当s=0 时,s=I 即编号为15 的人出圈),…最后一个出圈
者编号在位置1 中。

原创粉丝点击