Merge pull request #488 from luojianp/patch-2

Update 剑指 offer 题解.md
This commit is contained in:
郑永川 2018-12-08 16:03:17 +08:00 committed by GitHub
commit f636f9c8bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -2826,6 +2826,55 @@ public int LastRemaining_Solution(int n, int m) {
return (LastRemaining_Solution(n - 1, m) + m) % n;
}
```
## 解题思路详解
创新的解法,采用数学公式推导找到规律,推导过程如下:
Step1.
首先定义一个关于 m 和 n 的函数f(n, m)表示在n个数字 0,1,2…,n-1 中每次删除第m个数字最后剩下的那个数字。
注意f(n, m)表示的是在经过了多次删除后最后剩下的那个数字也就是说f(n,m)本质上是个数。
Step2.
n个数中第一个被删除的数是(m - 1) % n把它记为k此时数组中还剩下0,1,2… k-1, k+1,…n-1 这几个数字。接下来从k+1开始计数相当于在剩下的序列中k+1排在最前面从而形成k+1,… n-1,… 0,1,2…k-1。本题中第一个被删除的是数字2因此数组还剩下 0,1,3,4,5相当于3,4,5,0,1。由于这个序列是不连续的在2那个地方断开了所以不能写为f(n-1, m)。在此记为g(n-1, m)。
最初序列最后剩下的数字一定是删除一个数字后剩下的数字因此f(n, m) = g(n-1, m)
Step3.
将[3,4,5,0,1]重新映射为[0,1,2,3,4]映射的公式是p(x) = (x - k - 1) % n。其中k = 2n = 6。
3 —-> 0
4 —-> 1
5 —-> 2
0 —-> 3
1 —-> 4
映射以后的序列是0,1,2,3,4是一个连续的序列因此可以用f(n-1, m)来表示。
此时的f(n-1, m)是不等于g(n-1, m),因为二者对应的序列不同,存在一个映射关系。
该映射的逆映射是 p(x) = (x + k + 1) % n
因此g(n-1, m) = p[f(n-1, m)] = [f(n-1, m) + k + 1] % n
f(n, m) = [f(n-1, m) + k + 1] % n
带入k = (m - 1) % n, 得到f(n, m) = [f(n-1, m) +m] % n (n > 1)
Step4.
当n = 1时只有一个人此时剩余的数字为0
综上,约瑟夫环的公式是:
f(n, m) = 0 (n = 1)
f(n, m) = [f(n-1, m) +m] % n (n > 1)
```java
public class Solution {
public int LastRemaining_Solution(int n, int m) {
// 不合法输入的判断
if(n == 0 || m < 1) {
return -1;
}
int last = 0;
for(int i = 2; i <= n; i++) {
last = (last + m) % i;
}
return last;
}
}
```
# 63. 股票的最大利润