一、问题描述:
设有
N
个人围坐一圈并按顺时针方向从
1
到
N
编号,从第
S
个人开始进行
1
到
M
报数,报数到第
M
的人,此人出圈,再从他的下一个人重新开始从
1
到
M
报数,如此进行下去,每次报数到M
的人就出圈,直到所有人都出圈为止。给出这
N
个人的出圈顺序。
举一个简单的例子来更好的理解题目,假设有10(
N)个人,从第3(
S)个人开始进行1到2(
M)报数,报数到第二个人出圈,
再从他的下一个人重新开始从
1
到
M
报数,直到所有人出圈,给出这10个人出圈顺序。
图中红色数字代表座位顺序,绿色数字为第一次循环出圈的人以及顺序,紫色数字为第二次出圈的人以及顺序,蓝色、黄色分别为第三次、第四次。我们的目的是求出
红色数字的出圈顺序
。
出圈顺序为:
4 6 8 10 2 5 9 3 1 7
二、两种解题思路(均为数组法)
第一种:
public class Joseph {
public static void main(String[] args) {
final int N = 10,S = 3,M=2; //定义基本数据
int i = S-1,j,k=10,g=1;
int[] a = new int[10]; //定义容量为十人的数组
for(int h = 1 ;h<=10;h++){
a[h-1] = h;} //将十个人对应的序号输入数组中
System.out.print("\n出圈的顺序为:");
do
{
i = i + (M-1); //直接定位到出圈人的下标
while(i >= k){ //使i能循环进行
i = i-k;
}
System.out.println(" "+a[i]); //打印出圈人的序号
for(j = i;j<k-1;j++){
a[j] = a[j+1];} //a[i]出圈后,a[i]后的人要向前移一位
k--; //剩下总人数减一
g++; //出圈人数加1
}while(g<=10); //循环在所有人出圈后停止
}
}
定义一个while循环,循环在每一次执行时会排一人出圈,并且其余人构成下一次循环体(例如第一次循环后其余九人围成圆圈),在所有人出圈后,循环停止。并且每次有人出圈后都会即时打印出圈人序号。
第二种:
public class Joseph2 {
public static void main(String[] args) {
final int N = 10,S = 3,M = 2;
int []p = new int[10]; //定义数组用来标记出圈人
int []q = new int[10]; //定义数组用来接收出圈人的序号
int k,n = 0;
k = 1; //从1开始数数
for(int i = 1;i<= 10;i++){
//第一个for循环每循环一次增加一个出圈人
//p[k]用来标记出圈人,没循环一次p[k]标记一次
for(int j = 1;j<=2;j++){
if(k == N-1){
k = 0; //当数到最后一人时,回到第一个人开始数
}
else{
k++; //数数
}
if(p[k]==1){
j--; //如果数到了出圈人,此次循环作废,循环次数加一
}
}
p[k] = 1; //标记出圈人
q[n++]=k+1; //接收出圈人序号
}
System.out.println("出圈顺序为:");
for (int i = 0; i < N; i++) {
System.out.print(q[i]+" "); //遍历序号
}
}
}
数数法,两个for循环会从1开始数数,并且数到出圈人会将其标记,直到所有人都被标记之后,循环结束。
“约瑟夫环”还有更多解法,笔者能力有限,本篇也仅供参考。