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

posted on 2012-08-02 16:05  mycapple  阅读(417)  评论(0编辑  收藏  举报

导航