约瑟夫环问题

一、介绍

约瑟夫环(Josephus Problem) 是一个著名的数学和计算机科学问题,大致意思是:N个人围成一圈,从第一个人开始报数,报到第M个人就将其淘汰出圈,然后从下一个人重新开始报数,如此循环,直到剩下最后一个人。求这个幸存者的初始位置。

题目链接:孩子们的游戏(圆圈中最后剩下的数)

二、解决方案

1. 模拟

该方法是最直观的,就是开辟一个数组记录哪些人被淘汰,一直遍历下去直至只剩下一个人。

cpp 复制代码
class Solution {
public:
    int LastRemaining_Solution(int n, int m) {
        vector<bool> check(n, true);

        int cnt = 0, idx = 0, alive = n;
        while(alive > 1)
        {
            if(check[idx])
            {
                cnt++;
                if(cnt == m)
                {
                    cnt = 0;
                    alive--;
                    check[idx] = false;
                }
            }
                
            if(++idx == n) idx = 0;
        }

        for(int i = 0; i < n; i++)
            if(check[i])
                return i;

        return -1;
    }
};

2. 递归

上述的方法确实可以解决问题,但是时间复杂度会有点高,下面介绍一下很巧妙的方法。它的核心是假设一共有 n 个人,在淘汰了一个之后就只剩下了 n - 1 个人,那么我们是不是可以将 n - 1 个人看作一个新的约瑟夫环问题。这个思想就是递归的核心!

上面我们在了解了思想后来想几个关键问题。当人数从 n 变成 n - 1 的时候,下一次淘汰人需要从这一次淘汰人的后一位开始计数,这时会发现好像下一个人的编号不一定是从 0 开始的呀!所以这里需要做一下映射。我们假设 k 是这一次淘汰的编号( k = (m - 1) % n ),那么下一次开始的位置就是 k + 1,它对应的新编号是 0、以此类推 k + 2 -> 1、k + 3 -> 2...。具体可以看下面 n=5, m=3 的例子:

黑色那一圈是初始 n 人旧编号,淘汰下标 k 之后,蓝色是剩下 n−1 人的新编号。所以可以推出:旧编号 = 新编号 + k + 1,但这对吗?很明显在上图中 k+3 已经超过原来的范围了所以实际是出:旧编号 = (新编号 + k + 1) % n 。这样看是不是就成了比原来更小一层的问题了。这里可以将 k = (m - 1) % n 公式合并到那个公式中,可以得到:旧编号 = (新编号 + m) % n。

想一个边界情况,如果 n 等于 1 呢?这时其实就可以直接返回因为只有一个人,而幸存者编号就是 0。所以递推公式如下( f(x)表示人数为 x 时幸存者的编号):

递归实现:

cpp 复制代码
class Solution {
public:
    int LastRemaining_Solution(int n, int m) {
        if(n == 1)
            return 0;

        return (LastRemaining_Solution(n - 1, m) + m) % n;
    }
};

dp实现:

cpp 复制代码
class Solution {
public:
    int LastRemaining_Solution(int n, int m) {
        vector<int> dp(n + 1);
        for(int i = 2; i <= n; i++)
            dp[i] = (dp[i - 1] + m) % i;

        return dp[n];
    }
};
相关推荐
晒太羊的猫5 小时前
hot100动态规划刷题
算法·动态规划
weixin_531094696 小时前
heap_4内存管理
java·开发语言·算法
余额瞒着我当琳6 小时前
红黑树的实现--红黑树的概念、规则、效率、结构、旋转、查找、验证
数据结构·c++·算法
程序员南飞6 小时前
win系统安装minio
java·数据结构·算法
青少儿编程课堂6 小时前
AC自动机多模式串匹配精讲:校园关键词巡检实战解析
c++·python·算法·bfs·信息学竞赛
longlongzihan7 小时前
LeetCode 5. 最长回文子串:从暴力匹配到中心扩展的思维演进
c++·算法·leetcode
waoooqwe7 小时前
问卷样本真实吗
大数据·数据库·算法·数据分析
苦瓜打怪兽7 小时前
leetcode 热题100(11. 盛最多水的容器)
python·算法·leetcode
w_zero_one8 小时前
链表(4)
java·数据结构·算法
吠品8 小时前
GitHub 周榜拆解:决策小模型扎堆上位
c语言·开发语言·算法