OpenJudge 2746 约瑟夫问题 1
1 #include<stdio.h> 2 typedef struct Node 3 { 4 int data; 5 Node *next; 6 Node(int i){ //Node函数 7 data=i; 8 next=NULL; 9 } 10 }node; 11 int main() 12 { 13 int i,m,n; 14 node *head,*p,*q; 15 while(scanf("%d%d",&n,&m),m||n) 16 { 17 if(m==1) //这点需要注意,m==1时,特殊情况 18 { 19 printf("%d\n",n); 20 continue; 21 } 22 head=new Node(1);//初始化 23 for(p=head,i=2;i<=n;i++)// 正序输入链表元素 24 { 25 q=new Node(i);//初始化 26 q->next=p->next; 27 p->next=q; 28 p=q; 29 } 30 p->next=head;//构造循环链表,实现首尾相连 31 for(p=head;p->next!=p;p=p->next)//删除m的元素 32 { 33 for(i=1;i<m-1;i++) 34 p=p->next; 35 q=p->next; 36 p->next=q->next; 37 delete q; 38 }#include<stdio.h> 39 typedef struct Node 40 { 41 int data; 42 Node *next; 43 Node(int i){ //Node函数 44 data=i; 45 next=NULL; 46 } 47 }node; 48 int main() 49 { 50 int i,m,n; 51 node *head,*p,*q; 52 while(scanf("%d%d",&n,&m),m||n) 53 { 54 if(m==1) //这点需要注意,m==1时,特殊情况 55 { 56 printf("%d\n",n); 57 continue; 58 } 59 head=new Node(1);//初始化 60 for(p=head,i=2;i<=n;i++)// 正序输入链表元素 61 { 62 q=new Node(i);//初始化 63 q->next=p->next; 64 p->next=q; 65 p=q; 66 } 67 p->next=head;//构造循环链表,实现首尾相连 68 for(p=head;p->next!=p;p=p->next)//删除m的元素 69 { 70 for(i=1;i<m-1;i++) 71 p=p->next; 72 q=p->next; 73 p->next=q->next; 74 delete q; 75 } 76 printf("%d\n",p->data); 77 } 78 return 0; 79 } 80 81 82 printf("%d\n",p->data); 83 } 84 return 0; 85 } 86
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 中。