目录

17、剑指 Offer 62. 圆圈中最后剩下的数字

一、题目

剑指 Offer 62. 圆圈中最后剩下的数字 难度简单

0,1,···,n-1 这 n 个数字排成一个圆圈,从数字 0 开始,每次从这个圆圈里删除第 m 个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。

例如,0、1、2、3、4 这 5 个数字组成一个圆圈,从数字 0 开始每次删除第 3 个数字,则删除的前 4 个数字依次是 2、0、4、1,因此最后剩下的数字是 3。

示例 1:

输入: n = 5, m = 3
输出: 3

示例 2:

输入: n = 10, m = 17
输出: 2

限制:

  • 1 <= n <= 10^5
  • 1 <= m <= 10^6

二、解法

2.1、动态规划

核心思路

设 dp[n,m] 表示在 n 个数字序列(0~n-1)中不断删除第 m 个数字后,最后剩下那一个数。

假设 n=5,m=3,则 dp[5,3] 的初始化数组是:

0 1 2 3 4

若 n=4,m=3,则 dp[5-1,3] 也就是 dp[4,3] 的初始化数组是:

0 1 2 3

从初始化数组可以看出,dp[5,3] 和 dp[4,3] 最终的结果是不一样的。而 dp[5,3] 删掉一个数字后的数组应该是:

3 4 0 1

这个数组咱们给他取名为 dp’[4,3],它是不是跟 dp[4,3] 有一些联系呢?咱们把他俩的初始数组放在一起比较:

dp[4,3]:0 1 2 3

dp’[4,3]:3 4 0 1

从上面可能还是看不出具体区别,因此我们来看 dp[n-1,m] 和 dp’[n-1,m]的初始数组区别:

dp[n-1,m] 0 1 n-1-m n-m n-3 n-2
dp’[n-1,m] m m+1 n-1 0 m-3 m-2

观察上面的表格,我们发现 dp[n-1,m] 和 dp’[n-1,m] 的初始数组有如下规律:

dp'[n-1,m] = ( dp[n-1,m] + m ) % n

因为 dp[n,m] = dp’[n-1,m],所以咱们就找出了 dp[n,m] 到 dp[n-1,m] 之间的映射关系,也就是:

dp[n,m] = ( dp[n−1,m] + m ) % n

考虑边界条件,n 肯定是要大于1的;此外当 n=1 时,就只剩下一个数了,那就是 0。因此得出状态转移方程为:

dp[n,m]=0, n=1

dp[n,m] = ( dp[n−1,m] + m ) % n, 1<n

观察状态转移方程式,发现 m 是不变的而且 dp[n,m] 只跟 dp[n-1,m] 有关,因此可以直接使用一个变量来代替 dp[n,m],然后从下往上递推实现。

另外,因为 dp[5,3] 的初始数组是 0 1 2 3 4,值与下标相同,即上述规律适用于数组下标。

复杂度分析

时间复杂度:O(n)

空间复杂度:O(1)

Code

class Solution {
    public int lastRemaining(int n, int m) {
        int answer = 0;

        for (int i = 2; i <= n; i++) {
            answer = (answer + m) % i;
        }

        return answer;
    }
}

REF

https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/solution/tai-jiao-bi-ye-ye-neng-dong-dong-tai-gui-zmwj/