约瑟夫环:
N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。
- 例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3。
123451234512345依次报数,6报完后回到1继续
第一次报数:
1【1】 2【2】 3【3】 4【4】 5【5】 6 (5被杀掉,留下1 2 3 4 6)
第二次报数(上一次从1结束,因此此时从6开始继续报数):
6【1】 1 【2】 2【3】 3【4】 5【5】(4被杀掉,留下1 2 3 6)
第三次报数(上一次从4结束,因此此时从6开始继续报数):
6【1】 1【2】 2【3】 3【4】 6【5】 (6被杀掉,留下 1 2 3)
第四次报数(上一次从6结束,因此此时从1开始继续报数):
1【1】 2【2】 3【3】 1【4】 2【5】 (2被杀掉,留下 1 3)
第五次报数(上一次从2结束,因此此时从3开始继续报数):
3【1】 1【2】 3【3】 1【4】 3【5】 (3被杀掉,留下 1)
登山选队长问题
- 题目:
今天同学们相约一起爬山游玩,为了更好的进行这场活动,大家准备推举一个人作为出游的临时队长。 为了体现合理公平,大家提出了一个比较有趣的规则:
所有人围成一圈,顺序排号。从第一个人开始报数(从 1 到 3 报数),凡报到 3 的人退出圈子,剩下的人继续报数,最后留下的当选为队长。请你通过编写程序,求出一组人中的队长是原来第几位同学。 - 思路:(与约瑟夫环同理)
①先输入同学(记为数组student)的人数N
②初始化:
1)给定student数组默认初始值为1
2)定义flag来记录报的数
3)定义out来记录出圈的人数(当出圈人数为N-1时,表示只剩下最后一位同学,即队长)
③循环:(用i表示当前学生的下标)
1)循环条件:当只剩下最后一位同学时结束循环,否则i++
2)当student[i]=1时表示学生没有出圈,进行报数(flag++)
3)当报到3(flag=3)时,出圈(student[i]=0),重新从1开始计数(flag=0,由于学生存在时才flag++,因此flag从0开始)
4)如果下标i等于人数N,表明本轮循环结束,重新从头开始报数
④对student数组进行遍历,如果值为1(student默认为1,出圈记为0),则表示为剩下的最后一位同学,即为队长 - 代码如下:
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.print("请输入同学的人数:");
int num = input.nextInt();//同学的人数
int[] student = new int[num];
for (int i = 0; i < student.length; i++) {
student[i] = 1;//每个同学都为1,当退出圈子时记为0,方便接下来的判断
}
int flag = 0;//记录报数
int out = 0;//记录出圈的人数
int i = 0;//记录student的下标
while(out != num - 1) {//当留下一位同学时,即为队长
if(student[i] == 1) {
flag ++;
}
if(flag == 3) {
student[i] = 0;//出圈,标记为0
flag = 0;//重新开始报数
out++;//出圈的人数+1
}
i++;
if(i == num) {
i=0;
}
}//end while
/*do-while循环也能实现(第一次写用的do-while,与while用法大同小异)
do {
if(student[i] == 1) {//该学生还没有退出圈子
flag++;//
if(flag == 3) {//数到3,出圈
student[i] = 0; //出圈标记为0
out ++;//出圈人数+1
flag = 0;//继续从1开始(前面为flag++,故此处置为0)
}
}
i++;
if(i == num) {//如果是最后一位同学,则重新从头开始报数
i = 0;
}
}while(out !=num - 1);//只剩下最后一位同学时,结束循环
*/
for (int j = 0; j < student.length; j++) {
if(student[j] != 0) {//退出圈子的同学为0,最后一位留在圈子中的同学为1
System.out.println("队长为第" + (j+1) + "位同学");//下标从0开始,故第i位同学对应下标为j+1
}
}//end for
}
}