约瑟夫环
例如:N=11,M=5:
11(1-11)人中,每次报数5(间隔)的出列;
参数:N=11,M=5:
规律:F (N, M) = (F (N-1,M) + M) % N ;
说明:黄色表示当前出队人,橙色表示下一个出人,红色表示最后存活人。
自顶向下过程:
第1行:当前编号为5的人(下标为4)出列,此时下一个出列人编号为10(下标为9),可是由于从当前出列人下一位开始重新编号,所以编号为10的出列人在重新编号后出队位置变成了下一行中的下标4;下标向前移动了5(就是间隔M)个位置;
第2行:当前出队人编号10(下标4),下一个出队人4(下标9,重新编号下标前移5,为4);
第3行;当前出队人编号4(下标4),下一个出队人11(下标0,循环重编下标前移5,为4);
第4行;当前出队人编号11(下标4),下一个出队人7(下标1,循环重编下标前移5,为4);
第5行;当前出队人编号7(下标4),下一个出队人3(下标2,循环重编下标前移5,为4);
第6行;当前出队人编号3(下标4),下一个出队人2(下标3,循环重编下标前移5,为4);
第7行;当前出队人编号2(下标4),下一个出队人6(下标0,循环重编下标前移5,为0);
第8行;当前出队人编号6(下标0),下一个出队人9(下标2,循环重编下标前移5,为1);
第9行;当前出队人编号9(下标1),下一个出队人1(下标2,循环重编下标前移5,为0);
第10行;当前出队人编号1(下标0),下一个出队人8(下标1,循环重编下标前移5,为0);
第11行;只剩一个,当前存活8,下标0;
到此时,找到了最终的存活者,下面我们由最后一步逆推回去,看看最后存活的人的下标对应的在最开始的时候是哪个下标。例如:此题我们一眼就可以看出最后存活下标为0,对应编号为8的人,而显然编号为8的人在一开始的下标是7。但是如何从结束下标逆推到开始下标呢?
自底向上的逆过程
F (N, M) = (F (N-1,M) + M) % N
f (1, 5) = 0 : 表示1个人中报数5,只有一个人,胜利者下标为0;
f (2, 5) = (f (1, 5) + 5) % 2 = (0 + 5) % 2 = 1: 在有2个人的时候,胜利者的下标位置为1
f (3, 5) = (f (2, 5) + 5) % 3 = (1 + 5) % 3 = 0
f (4, 5) = (f (3, 5) + 5) % 4 = (0 + 5) % 4 = 1
f (5, 5) = (f (4, 5) + 5) % 5 = (1 + 5) % 5 = 1
f (6, 5) = (f (5, 5) + 5) % 6 = (1 + 5) % 6 = 0
f (7 ,5) = (f (6, 5) + 5) % 7 = (0 + 5) % 7 = 5
f (8 ,5) = (f (7, 5) + 5) % 8 = (5 + 5) % 8 = 2
f (9, 5) = (f (8, 5) + 5) % 9 = (2 + 5) % 9 = 7
f (10, 5) = (f (9, 5) + 5) % 10 = (7 + 5) % 10 = 2
f (11, 5) = (f (10, 5) + 5) % 11 = (2 + 5) % 11 = 7
参考代码
public int LastRemaining_Solution(int n, int m) { if (n < 1 || m < 0) return -1; //return core(n, m)+1;//游戏者的编号从1开始 return core(n, m);//游戏者的编号从0开始 } public int core(int n, int m) { if (n == 1) return 0; return (core(n - 1, m) + m) % n; }