问题的描述
N个人,编号为1~N,从第一个人开始报数到M,报到M的人移除,剩下的人从被移除的人后面继续从一到M报数,报到M的移除,依次类推,求依次被移除的人的编号。
方法一
使用队列来解决。
因为我们处理的是n个元素里面的第m个元素,如果每次从队列里一边取元素,一边又加到队列的末尾,数到第m的时候,这第m的元素直接出队,不再入队。依此循环n遍,可以按所需顺序移除掉n个元素。
C++代码如下:
1 #include2 #include 3 using namespace std; 4 5 struct Node 6 { 7 int data; 8 struct Node* next; 9 }; 10 typedef struct Node* queue; 11 typedef struct Node* position; 12 //创建队列 13 queue create_queue(int N) 14 { 15 queue Q = (queue)malloc(sizeof(Node)); 16 Q->next = NULL; 17 position r=NULL; 18 for (int i = 1; i <= N; i++) 19 { 20 position p = (position)malloc(sizeof(Node)); 21 p->data = i; 22 if (Q->next == NULL) 23 Q->next = p; 24 else 25 r->next = p; 26 r = p; 27 } 28 r->next = NULL; 29 return Q; 30 } 31 //出队 32 position Dequeue(queue Q) 33 { 34 if (Q->next == NULL) 35 { 36 cout << "queue is empty" << endl; 37 exit(1); 38 } 39 else 40 { 41 position front = Q->next; 42 Q->next = front->next; 43 front->next = NULL; 44 return front; 45 } 46 } 47 //入队 48 void Enqueue(queue Q,position p) 49 { 50 position r=Q; 51 position rear = (position)malloc(sizeof(Node)); 52 if (rear == NULL) 53 { 54 cout << "out of space"; 55 exit(1); 56 } 57 while(r ->next!= NULL) 58 r = r->next; 59 r->next = rear; 60 rear->data = p->data; 61 rear->next = NULL; 62 free(p); 63 } 64 //Josephus 65 void Josephus(queue Q, int N,int M) 66 { 67 position p,r=Q; 68 for (int i = 0; i //循环N次 69 { 70 for (int j = 0; j < M-1; j++)//出队和入队M-1次 71 { 72 p=Dequeue(r);//出队 73 Enqueue(r,p);//入队 74 } 75 p = Dequeue(r);//出队第M次结点,但不再入队 76 cout< data<<" "; 77 } 78 } 79 int main() 80 { 81 queue Q=create_queue(10); 82 Josephus(Q,10,5); 83 system("pause"); 84 return 0; 85 }
运行结果:
方法二
使用递归。
从数学的角度考虑,N个人围坐在一起,编号为0~N-1(注意这里从0开始编号),从中移除第M个人后,剩下的N-1个人从上一次被移除的人后面重新开始编号,即从0~N-2。依次类推,直至只剩下最后一个人,编号为0。举个例子,有5个人围坐在一起,M=4,编号变化情况如下:
5人 0 1 2 3 4
4人 1 2 3 0
3人 1 2 0
2人 0 1
1人 0
编号之间存在数学关系,
index(n)=(index(n-1)+M)%n
即某个人在n人环时的编号index(n)与其在n-1人环时的编号index(n-1)之间的关系。
还有个比较重要的信息,就是N人环中第一个被移除的人的编号必为(N+M-1)%N。N人环第二个被移除的人的编号可以从N-1人环第一个被移除的人的编号根据上述公式推出,N人环第三个被移除的人的编号可以从N-1人环第二个被移除的人的编号推出,而N-1人环第二个被移除的人的编号又可以从N-2人环第一个被移除的人的编号推出……依此类推可以求出N人环第k次被移除的人的编号。(一句话,N人环第二个被移除的人和N-1人环被移除的人是同一个人,只是编号不同,编号关系即为根据上述公式)
代码如下:
1 #include2 using namespace std; 3 4 int Josephus(int N,int M,int k)//计算出第k次被移出元素的编号 5 { 6 if (k == 1) 7 return (N + M - 1) % N; 8 else 9 return (Josephus(N - 1, M, k - 1) + M) % N; 10 } 11 int main() 12 { 13 for (int k = 1; k <=5; k++) 14 cout << Josephus(5, 4, k)+1 << " ";//加1是为了使编号是从1开始而不是0 15 system("pause"); 16 return 0; 17 }
运行结果: