问题描述:n个人(编号1~n),从1开始报数,报到m的退出,剩下的人继续从1开始报数。求胜利者的编号
分析:
从问题来看,首先我们假设有个数组,那么第一圈(0到n)的报数结束之后,剩下的人继续报数,可是第一圈出去的人仍然在数组里面,这样肯定会影响到编号的计算,一个方法就是出去的人的位置清0,比如第一次出去的是arr[2],那么arr[2]的值就变成0,下一次计算的时候忽略掉这个标记的位置。
如果不用数组,整个遍历的过程就很容易想起来链表,退出的人则直接移除节点,剩下的人继续报数那么意思是最后一个人的next指针指向第一个人,这样就是符合题目的流程。注意循环结束的条件。
1 | public class Test { |
下面是数学上的解法,我们假设有5个人参与,每报3则出列,具体的过程如下:
1 12345 – 3出列,从4号继续开始报数
2 4512 – 1出列,从2继续开始报数
3 245 – 5出列,24继续报数
4 24 – 2出列,剩下一个4
5 4
n个人参与的时候,出列一个人,剩下的n-1继续构成新的约瑟夫环,因此n个人的问题最后肯定可以递归的转化成1个人的问题,最后这个人在1个人的情况下就是最后生存的人,它此时的编号是1,问题是这最后一个人的在n人环里面的编号是多少?
观察上面的步骤,5个人的约瑟夫环,3出列后,让4号做开头,重新构成的新的4个人的环,它们有这样一个对应关系,
5人环里面的编号4的那个人,变成了4人环里面编号1的人
5人环里面的编号5的那个人,变成了4人环里面编号2的人
5人环里面的编号1的那个人,变成了4人环里面编号3的人
基于这个过程,我们可以做出这样一个推导f(5) = f(4) + 3 % 5,其中f(5)表示5人环里面的人的编号,f(4)表示4人环里面的人的编号,这个公式表示它们之间的对应关系。除5因为这是5人环。
同样的,我们也可以得出f(4) = (f(3) + 3) % 4,f(3) = (f(2) + 3) %3,f(2) =( f(1) + 3)%2,f(1) = 1 ;
在1人的情况下,生存的是人编号一定是1,
2人的时候,它对应的编号是 1+3 % 2 = 2
3个人的时候,生存的人对应的编号是2+3%3 = 2
…
n个人的时候,对应的是f(n) = f(n-1) + 3 % n,
我们把m带入进去,f(n) = (f(n-1) + m) % n
1 | static int joseph(int n, int m) { |
结果与上面相同。