目录
题目描述
n 个人的编号是 1~n,如果他们依编号按顺时针排成一个圆圈,从编号是1的人开始顺时针报数。
(报数是从1报起)当报到 k 的时候,这个人就退出游戏圈。下一个人重新从1开始报数。
求最后剩下的人的编号。这就是著名的约瑟夫环问题。
本题目就是已知 n,k 的情况下,求最后剩下的人的编号。
输入格式
题目的输入是一行,2个空格分开的整数n, k
约定:0 < n,k < 1百万
输出格式
要求输出一个整数,表示最后剩下的人的编号。
样例输入
10 3
样例输出
4
解题思路
(事先声明:通过查阅找到约瑟夫环的递推公式,有了这个公式解题可以保证运行不超时。)
先分析一下此约瑟夫问题,此题要求最后剩下的人的编号,实际上就是求最后一个出列(退出游戏)的人的编号。编号从0开始。
首先给出约瑟夫环的递推公式:f(n,m)=[f(n-1)+m]%n ,(n>1)
n表示参与约瑟夫环的总人数,m表示报到数字k的第几人出列。
接下来分析一下公式如何得出的:
利用递归的思想,解决n个人报数需要先解决(n-1)个人报数的问题,解决(n-1)个人报依次数问题需先解决[(n-1)-1]个人报数的问题,依次类推到最初情况应该是n=1,只有1个人的情况。
假设n=1时,可得到f(n,m)=f(1,m)=0 , 因为此时我们可以知道无论m等于多少最后出列的人都是编号为0的人,因为场上只有编号为0的人。
假设n=2,报到数字m的人出列。由于编号从0开始,所以(m-1)号相当于是从第一个报数开始时的第m个人(也就是报到数字m的人)。此时最后出列的人应该是n=1即只有1个人报数时的最后出列的序号加上m。
可得到f(2,m)=f(1)+m。
思考到这里,我们还需要考虑所报数字大于剩下的人数的情况,在上面的基础上再假设m=3,计算可得:f(2,m)=f(2,3)=0+3=3,很明显只有两个人,没有编号为3的人。此时我们想由于是环状报数,当2个人报完以后需要从编号为0的人开始接着报数。所以我们可以对所求的f(2,m)的值进行求模运算(即求出在所有人中来回报数后,不足一个轮回了还剩下多少人,剩下的人当中最后必定会报出所报数字m)。
即算式应该为:f(2,m)=f(2,3)=[f(1)+3]%2=[0+3]%2=1。
所以最后编号为1的人出列,即最后剩下编号为1的人(可自己手动模拟验证)。
同理当n=3,m=3时,算式为:f(n,m)=f(3,3)=[f(2,3)+3]%3=[1+3]%3=1
所以最后编号为1的人出列,即最后剩下编号为1的人(可自己手动模拟验证)。
同理当n=4,m=3时,算式为:f(n,m)=f(4,3)=[f(3,3)+3]%4=0
......
据此可推导出参与报数的总人数为n时,公式为:f(n,m)=[f(n-1)+m]%n
代码实现
import java.util.Scanner;
public class Main{
public static void main(String[]args){
Scanner sc = new Scanner(System.in);
while (sc.hasNextInt()) {
//hasNextInt()方法是判断控制台接收是否为整型数字,而不是接收数据。
int n=scanner.nextInt();//n表示参与约瑟夫环的总人数
int m=scanner.nextInt();//m是报到数字k的第几人出列
int num=0;
//i表示递归状态中的层次,从i=2开始表示f(2,m),即第二层的总人数为2,依次循环3,4,...,n
for (int i = 2; i <=n; i++) {//当i=1时即总人数为1个人报数时编号num为0,不参与循环
num=(num+m)%i;//即计算公式f(n,m)=[f(n-1)+m]%n
}
System.out.println(num+1);
}
}
}