41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到剩下最后一个人为止。
解法一:队列解法
实质上是将所有人按编号入队,当数到三时此人直接出队,表示已经死亡,同时将k置为1,表示重新计数。其他时候将该人先出队后入队即可,表示进入新的一轮,直至队列元素为1结束。
采用循环队列,循环结束标志为:
Q -> rear != (Q - >front + 1) % 100
即只剩下一个元素的时候
源代码
#include<stdio.h>
#include<stdlib.h>
typedef struct
{
int *data;
int front,rear;
int queuesize;
int length;
}Queue;
void Init (Queue *Q) //构造一个队列
{
Q->queuesize = 100;
Q->data = (int *)malloc(Q->queuesize*sizeof(int));
Q->front = Q->rear = 0;
Q->length = 0;
}
void build (Queue *Q) //为队列赋值
{
int i;
for(i=1;i<=41;i++)
{
Q->data[Q->rear] = i;
Q->length++;
Q->rear = (Q->rear+1)%100;
}
//printf("\n------%d-----\n",Q->length);
Q->front = 0;
}
void EnQue (Queue *Q,int e) //入队
{
if((Q->rear+1)%100 != Q->front)
{
Q->data[Q->rear] = e;
Q->rear = (Q->rear+1)%100;
}
}
void OutQue(Queue *Q) //出队
{
if(Q->rear != Q->front)
{
Q->front = (Q->front+1)%100;
}
}
void display(Queue *Q)
{
if(Q->rear != Q->front)
{
printf("%d ",Q->data[Q->front]);
Q->front = (Q->front+1)%100;
}
}
void yuesefu(Queue *Q)
{
int k=1,e;
while(Q->rear!=(Q->front + 1)%100)
{
e = Q->data[Q->front];
if(k == 3)
{
display(Q);
k = 1;
Q->length--;
}
else
{
OutQue(Q);
EnQue(Q,e);
k++;
}
}
printf("\n最后剩下:%d", Q->data[Q->front]);
}
int main()
{
Queue Q;
Init(&Q);
build(&Q);
yuesefu(&Q);
return 0;
}
解法二:数组解法
本质上就是一个伪链表,用一个数组arr[]表示编号人员的状态,t代表死亡人数,每个人都遍历一次,从i = 0开始遍历,若arr[i] = 0, 说明人没死,计数cnt + 1, 当计数到3时,将
arr[i] = 1 ; 代表该人已经死亡
t++; 代表死亡人数+1
cnt = 0; 重新计数
当所有人遍历完后输出最后一个人即可。
源代码
#include<bits/stdc++.h>
using namespace std;
int main(){
int n = 41,m = 3;
bool arr[n+1];
for(int i = 0; i <= n + 1; i++)arr[i] = 0;
int t = 0,cnt = 0,i = 0;
while(t < n){
i++;
if(i>n) i=1;
if(!arr[i]) cnt++;
if(cnt==m){
cnt=0;
t++;
arr[i]=1;
if(t == n){
printf("\n最后剩下:%d", i);
}else{
printf("%d ",i);
}
}
}
return 0;
}
解法三:循环链表
循环链表可以更加简洁明了地描述约瑟夫环,创建一个41号元素的循环链表,从第一位开始遍历。 当数到三的前一位时,用temp指向自杀人结点
temp = p -> next;
直接由第i-1位指向第i+1位
p -> next=temp->next;
最后删除temp结点
free(temp);
最后将总人数-1,重复过程直至n == 1。
源代码
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
int n;
typedef struct node{ //循坏链表
int date;
struct node *next;
}node,*linklist;
linklist creat(int n){ //循坏链表初始化
linklist p,head;
head=(linklist)malloc(sizeof(node));//建立头结点
p=head;
linklist s;
int i=1;
if(n!=0)
{
while(i<=n)
{
s=(linklist)malloc(sizeof(node));
s->date=i++;
p->next=s;
p=s;
}
s->next=head->next; //尾节点跳过头结点直接指向第一个元素地结点
}
free(head); //删除头结点
return s->next;
}
int main()
{
n = 41;
int m=3;
int i;
linklist p=creat(n);
linklist temp;
m%=n;
while(n > 1)
{
for(i=1;i<m-1;i++)
{
p=p->next;
}
printf("%d->",p->next->date);
temp=p->next;
p->next=temp->next; //模拟约瑟夫环的淘汰过程
free(temp);
p=p->next;
n--;
}
printf("\n");
linklist h = p;
printf("最后剩下:%d ", p -> date);
return 0;
}
解法四:一步求解
如果只是求最后存活的人编号的化, 有如下公式:
p = (p+m) % i;
源代码
#include<bits/stdc++.h>
using namespace std;
int main(){
int n = 41,m = 3;
int p=0;
for(int i=2;i<=n;i++)p=(p+m)%i;
printf("最后活着的人的编号为%d\n",p+1);
}