约瑟夫环讲解

问题描述略过

最简单的是用队列模拟,没有死的人回到队列末尾,死的人移出队列,直到队列长度为1

例如:

11个人依次叫号,叫到3死

1,2,3,4,5,6,7,8,9,10,11 —>

2,3,4,5,6,7,8,9,10,11,1 —>

3,4,5,6,7,8,9,10,11,1,2 

3移出队列

4,5,6,7,8,9,10,11,1,2—>

5,6,7,8,9,10,11,1,2,4—>

6,7,8,9,10,11,1,2,4,5

6移出队列

7,8,9,10,11,1,2,4,5—>

...

直到队列仅剩一人: 7

用队列进行模拟,代码:

def solution(self, n: int, k: int) -> int:
    q = []
    for i in range(1, n + 1):
        q.append(i)
    while len(q) != 1:
        tmp = 0
        while tmp != k - 1:
            tmp += 1
            q.append(q.pop(0))
        q.pop(0)
    return q[0]

公式推导:

已知1,2,3,4,5,6,7,8,9,10,11 这11个人中最终胜利者为7号

那么1,2,3,4,5,6,7,8,9,10 这10个人中的最终胜利者是谁?

从11个人往10个人推导,即移除一个人后的队列情况

1,2,3,4,5,6,7,8,9,10,11 —>

4,5,6,7,8,9,10,11,1,2 

映射到10个人的情况就是:

1,2,3,4,5,6,7,8,9,10

最终胜利者为4

可以看到11个人时胜利者的索引位置为6

3号被移除后,10个人中胜利者的索引位置为3,胜利者的索引变化为6 - 3 =  3(叫号数)

由此我们可以从只有2个人的时候往回推n个人的情况

即 now = last + 叫号数k

为了防止边界溢出,还需对当前人数取余操作

我们用的是索引,返回结果时不要忘记再加一

代码:

def solution(self, n: int, k: int) -> int:
    res = 0
    for i in range(2, n + 1):
        res = (res + k) % i
    return res + 1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值