约瑟夫环问题

一、介绍

约瑟夫环(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];
    }
};
相关推荐
CodeRecycle1 小时前
Python 自动配置 pip 支持库(通过 Windows Bat 脚本)
算法
liliangcsdn1 小时前
特质偏度因子的计算示例和分析
算法
荆棘鸟智能2 小时前
无人机遥感图像实时拼接算法工程实践:从特征提取到全景融合的完整技术链路
算法·无人机
6Hzlia2 小时前
【Classic 150 刷题计划】 LeetCode 58. 最后一个单词的长度 | C++ 极简反向遍历与单变量状态机
算法
AgentMaster3 小时前
企业如何应用智能客服?5 个典型场景的技术架构与实施路径
大数据·算法
程曦曦3 小时前
MySQL 生产库误删 98 张表后的时间点恢复实战:从 binlog 解析到资金对账
linux·数据结构·其他·算法·ubuntu·运维开发
weixin_307779134 小时前
C++代码实现MATLAB中的ode45函数功能
开发语言·c++·算法·matlab
不穿鞋的懒羊羊4 小时前
dfs深度优先搜索
算法
OYYHXPJR4 小时前
【算法】区间重叠模板和矩形重叠模板
算法
shehuiyuelaiyuehao5 小时前
算法46,分治快排,排序数组
java·算法·排序算法·排序