剑指Offer:[第24天 数学(中等)]--->圆圈中最后剩下的数字


一、题目描述

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 <= 105
1 <= m <= 106


二、思路分析

注:思路分析中的一些内容和图片参考自力扣各位前辈的题解,感谢他们的无私奉献

约瑟夫环问题

约瑟夫是犹太军队的一个将军,在反抗罗马的起义中,他所率领的军队被击溃,只剩下残余的部队40余人。他们都是宁死不屈的人,所以不愿投降做叛徒。一群人表决说要死,所以用一种策略来先后杀死所有人。于是约瑟夫建议:每次杀死一个人,而被kill的人的先后顺序是由抽签决定的,约瑟夫有预谋地抽到了最后一签,在杀死了除了他和剩余那个人之外的最后一人,他劝服了另外一个没死的人投降了罗马。
对应的数学题目
一间房间总共有 n n n 个人(下标 0 ~ n − 1 0~n-1 0n1),最后只能有一个人活下来,按照如下规则去杀死人:所有人围成一圈顺时针报数,报到 m m m 的人将被杀死。被杀死的人将从房间内被移走,然后从被杀死的下一个人重新开始报数。继续将报到 m m m 的人杀死,移走,重复上面过程,直到只剩一人。你要做的是:当你在这一群人之间时,你必须选择一个位置以使得你变成那剩余的最后一人,也就是活下来。

核心思路

我们知道最后剩下一个人时,胜利者的下标是0。也就是我们已知胜利者最后的位置,要一直逆推到最初的时候胜利者所在的位置。我们用 f ( n , m ) f(n, m) f(n,m) 表示 n n n 个人报数,每报到 m m m 时杀掉那个人,返回最终胜利者的编号。
每次杀掉一个人,队伍的移动情况
现在我们让 m = 3 m=3 m=3,假设最后存活的人是 G G G,用绿色表示,每次杀死的人用红色表示。可以看到每杀死一个人, G G G 的位置都向前移动 m = 3 m=3 m=3 位。 于是我们可以暂时得出: f ( n , m ) = f ( n − 1 , m ) + m f(n,m)=f(n-1,m)+m f(n,m)=f(n1,m)+m。但是这不完全正确,因为每次向前移动穿过队首时,需要去队尾继续往前移动。也就是说,每次 + m +m +m 操作后,若超过当前的总人数 n n n 时,需要回到队伍头计数。 于是我们可以得出: f ( n , m ) = ( f ( n − 1 , m ) + m ) % n f(n,m)=(f(n-1,m)+m) \% n f(n,m)=(f(n1,m)+m)%n。从这个公式可以看出,其实这也就是一个状态转移方程,即 f ( n ) f(n) f(n) 可由 f ( n − 1 ) f(n - 1) f(n1) 得到, f ( n − 1 ) f(n - 1) f(n1) 可由 f ( n − 2 ) f(n - 2) f(n2) 得到,……, f ( 2 ) f(2) f(2) 可由 f ( 1 ) f(1) f(1) 得到。我们这里 f ( 1 ) = 0 f(1)=0 f(1)=0
在这里插入图片描述
案例分析
n = 5 , m = 3 n = 5, m = 3 n=5,m=3 时对应的模拟删除过程
在这里插入图片描述
复杂度分析:
时间复杂度 O ( N ) \rm{O(N)} O(N):状态转移循环N-1次使用O(N)时间,状态转移方程计算使用O(1)时间
空间复杂度 O ( 1 ) \rm{O(1)} O(1):使用常数大小的额外空间


三、整体代码

整体代码如下

int lastRemaining(int n, int m){
    int x = 0;
    for(int i = 2; i <= n; i++){
        x = (x+m)%i;
    }

    return x;
}

运行,测试通过
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知初与修一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值