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 中。

你可能感兴趣的:(open)