一刷352-dp-剑指 Offer 62. 圆圈中最后剩下的数字(e)

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

例如,012345个数字组成一个圆圈,从数字0开始每次删除第3个数字,
则删除的前4个数字依次是2041,因此最后剩下的数字是3---------------------
示例 :
输入: n = 5, m = 3
输出: 3

输入: n = 10, m = 17
输出: 2
 
限制:
1 <= n <= 10^5
1 <= m <= 10^6
---------------------
思考:
模拟整个删除过程最直观,即构建一个长度为 n 的链表,各节点值为对应的顺序索引;
每轮删除第 m 个节点,直至链表长度为 1 时结束,返回最后剩余节点的值即可。

模拟法需要循环删除 n - 1轮,每轮在链表中寻找删除节点需要 m 次访问操作(链表线性遍历),
因此总体时间复杂度为 O(nm) 。题目给定的 m, n取值范围如下所示,
观察可知此时间复杂度是不可接受的。
1≤n≤10^5
1≤m≤10^6
----------------------
实际上,本题是著名的 “约瑟夫环” 问题,可使用 动态规划 解决。

在这里插入图片描述
在这里插入图片描述

以 n = 5 , m = 3的示例如下图所示。

在这里插入图片描述
在这里插入图片描述

如下图所示,为 n = 5 , m = 3 时的状态转移和对应的模拟删除过程。

在这里插入图片描述

复杂度分析:
时间复杂度 O(n): 状态转移循环 n - 1次使用 O(n)时间,状态转移方程计算使用 O(1)时间;
空间复杂度 O(1): 使用常数大小的额外空间;
-----------------
代码:
根据状态转移方程的递推特性,无需建立状态列表 dp,而使用一个变量 x 执行状态转移即可。
---------------
class Solution {
    public int lastRemaining(int n, int m) {
        int res = 0;//结果集:即最后一个元素
        for (int i = 2; i <= n; i++) {
            res = (res + m) % i;
        }
        return res;
    }
}
/**
1、首先考虑第一轮, 数 m 个元素之后,要删除的元素的下标是多少,从 index 为 0 开始,
删除元素的下标就是 m - 1,因为 m 可能大于 n
所以应该是 (m - 1) % n,因为要逢 n 归零重新数,
比如一共 3 个元素,数 4 个元素之后删除一个,(4 - 1) % 3 = 0,也就是删除的是第 0 个元素
2、再考虑第二轮,第一轮删除之后,应该是从删除元素的下一个元素重新开始数,
删除元素的索引是 (m - 1) % n,那么重新开始的索引就是 m % n,
所以下一个要删除元素的索引就是 (m % n + m - 1) % n,
 假设一共只有 3 个数字,那么留下的那个元素的索引其实就是 (m % n + m) % n

如果有 4 个元素,那么当只有 3 个元素的时候最后留下的那个元素的索引其实就是现在重新开始数的位置
这就很明显可以用动态规划解法,用已知解求未知解,这里的第一轮的 m % n,其实就可以当作已知解,
也可以看做是只有一个元素的时候的结果

已知只有一个元素的时候,留下的就是他本身,索引为 0,那么最开始的解可以设为 dp[1] = 0
结合上述思路,动态转移方程就是 dp[i] = (dp[i - 1] + m) % i
 */

LC

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值