约瑟夫环问题
做题时遇到了类似问题,困扰了一会,就去查了一下,在此总结一下约瑟夫环问题
题目如下 :
n个人围成一圈,从第一个人开始报数,数到m的人出列,再由下一个人从1开始报数…以此类推,直到最后一个人退出,依次输出退出圈的人的编号。
假设有n=10个人, 每次报数到m=3
编号: 1 2 3 4 5 6 7 8 9 10
报数: 1 2 3 1 2 3 1 2 3 1
因为报道3的人都退出,所以编号为3,6,9的人都退出
然后我们假设退出的人标记为1, 没退出的标记为0,就有:
编号: 1 2 3 4 5 6 7 8 9 10
报数1:1 2 3 1 2 3 1 2 3 1
标记1:0 0 1 0 0 1 0 0 1 0
第3,6,9个同学退出圈圈, 并将它们标记为1
继续接着最后一名同学报的数字继续报数:
第1名同学接着第10名同学的1报出了2,已经退出的同学跳过不报数
编号: 1 2 3 4 5 6 7 8 9 10
报数2:2 3 1 2 3 1 2
标记2:0 1 1 0 0 1 1 0 1 0
这一次,第2,7名同学退出圈圈,并将它们标记为1
…
报数3:3 1 2 3 1
标记3:1 1 1 0 0 1 1 1 1 0
报数4: 2 3 1
标记4:1 1 1 0 1 1 1 1 1 0
报数5: 2 3
标记5:1 1 1 0 1 1 1 1 1 1
所以最后一个退出的是第4个数字
最后,代码如下:
# include <stdio.h>
int main(void)
{
int a[100] = {0};
int i = 0; // 每个同学的编号;如果有 10 个人,那第 7 名同学的编号 i = 7;
int k = 0; // 报的数字 1,2,3,1,2,3,1,2,3;
int n = 0;//有 n 名同学;
int s = 0; // 出列同学的个数;
scanf("%d", &n);
while(s != n){ //直到出列同学的人数和所有同学的个数相等时循环结束;
i++; //每次循环i+1,就是另一个人报数;
if(i > n) //如果同学的编号大于总人数,就重新从第一个同学开始,相当于一个圈圈;
i = 1;
if(a[i] == 0){ //a[i] == 0时,说明这些同学没有出列,继续报数;
k++; //报数的值会加 1
if(k == 3){//当报的数字为 3 之后,再让它从 1 开始报数 ,并且将该同学标记为 1 ,表示出列;
a[i] = 1;//出列
s++;//出列的同学个数 + 1 ;
k = 0;
printf("%d ", i); //输出出列同学的编号;
}
}
}
return 0;
}