diff --git a/notes/62. 圆圈中最后剩下的数.md b/notes/62. 圆圈中最后剩下的数.md index 3418466b..dab8101a 100644 --- a/notes/62. 圆圈中最后剩下的数.md +++ b/notes/62. 圆圈中最后剩下的数.md @@ -10,16 +10,24 @@ ## 解题思路 -约瑟夫环,圆圈长度为 n 的解可以看成长度为 n-1 的解再加上报数的长度 m。因为是圆圈,所以最后需要对 n 取余。 +约瑟夫环问题。设 f(n) 为 n 个人(编号 0~n-1)报到 m 出列时最后剩下的编号。第一轮出列的是编号 (m - 1) % n,之后从编号 m % n 开始报数,剩下 n - 1 个人。把他们从 m % n 开始重新编号为 0、1、2……,就变成了 n - 1 个人的同一个问题,答案 f(n - 1) 是新编号;新编号 x 对应的原编号为 (x + m) % n,因此: + +```text +f(1) = 0 +f(n) = (f(n - 1) + m) % n +``` + +例如 n = 5、m = 3,每一行是这一轮还剩下的人,从开始报数的人排起,上方是这一轮的新编号。最后剩下的 3 在每一行的新编号就是 f(5)~f(1),下一行到上一行编号加 3 再取余: + +

+ +num 最大为 105,递归的调用深度与 num 相同,因此从 f(1) 开始递推: ```java public int iceBreakingGame(int num, int target) { - if (num <= 0) { - return -1; - } - if (num == 1) { - return 0; - } - return (iceBreakingGame(num - 1, target) + target) % num; + int x = 0; /* f(1) = 0 */ + for (int i = 2; i <= num; i++) + x = (x + target) % i; /* f(i) = (f(i - 1) + target) % i */ + return x; } ``` diff --git a/notes/pics/offer62-josephus.png b/notes/pics/offer62-josephus.png new file mode 100644 index 00000000..6bf126f9 Binary files /dev/null and b/notes/pics/offer62-josephus.png differ