详解基于C++实现约瑟夫环问题的三种解法

yizhihongxing

详解基于C++实现约瑟夫环问题的三种解法

约瑟夫问题

约瑟夫问题是一个经典的问题,是一个圆圈里面有$n$个数字,从中每次删除第$m$个数字,求出每次删除的数字。简单的说,约瑟夫问题就是$n$个人围成一圈,从第一个人开始报数,报到$m$的人出圈,直到计算到最后一个人。

解法一:使用递推(模拟游戏过程)

思路:利用递归的思想模拟即可。假如最后剩下一个数据,则保留该数据,否则位置为 $s=(f+m)\ mod\ i $,$f$表示上一轮已经被删除数字的位置,$i$表示上一轮剩余数字个数,$m$为给定的数字。

代码:

int Josephus(int n, int m) {
    if (n == 1) {
        return 1;
    }
    return (Josephus(n - 1, m) + m - 1) % n + 1;
}

递归较为简便,但是处理的数据规模过大,会引起栈溢出等问题。

解法二:循环链表

思路:使用循环链表模拟过程,进行删除操作,直到链表剩下一个节点。

代码:

#include <iostream>
using namespace std;

struct Node {
    int val;
    Node* next;
};

int Josephus(int n, int m) {
    Node* head = new Node{1, nullptr};
    Node* cur = head;
    for (int i = 2; i <= n; i++) {
        cur->next = new Node{i, nullptr};
        cur = cur->next;
    }
    cur->next = head;
    while (head->next != head) {
        int cnt = m % (n--);
        if (cnt == 0) {
            cnt = n;
        }
        Node* pre;
        for (pre = head; pre->next != head; pre = pre->next) {
            if (--cnt == 0) {
                break;
            }
        }
        Node* temp = pre->next;
        pre->next = temp->next;
        head = temp->next;
        delete temp;
    }
    int res = head->val;
    delete head;
    return res;
}

int main() {
    cout << Josephus(5, 3) << endl;    // 输出3
    cout << Josephus(8, 4) << endl;    // 输出6
    return 0;
}

解法三:数学归纳

思路:可以通过利用数学归纳法来证明一种对称性,然后根据对称性来推导出约瑟夫环问题的答案。设 $f(n)$ 表示当有$n$个人时最后留下的人在数列中的位置,则:

当 $n=1$ 时,$f(1)=0$。

当 $n>1$ 时,将编号改为从 $0$ 开始,第一个人编号为 $0$ ,对 $n-1$ 取模,设结果为 $x$,则:

$$ f(n,m)=\left{\begin{aligned} & 0 & n=1 \ & \left(f(n-1,m)+m\right)\ \% \ n & n>1 \end{aligned}\right. $$

代码:

int Josephus(int n, int m) {
    int res = 0;
    for (int i = 2; i <= n; i++) {
        res = (res + m) % i;
    }
    return res + 1;
}

示例说明

例如,有 $5$ 个人 $(0, 1, 2, 3, 4)$ 依次报数,编号为 $0$ 的人从 $1$ 开始报数,假设报到 $3$ 的人出局,依次出局的顺序为 $2, 0, 4, 3$,因此最后剩下的是编号为 $1$ 的人。

可以通过调用以下代码进行验证:

cout << Josephus(5, 3) << endl;

输出为 $3$。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解基于C++实现约瑟夫环问题的三种解法 - Python技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • C++实现简单职工管理系统

    C++实现简单职工管理系统攻略 功能需求 我们需要实现一个简单的职工管理系统,其具有以下功能: 增加职工:可以手动输入职工信息,包括职工编号、职工姓名、职工岗位,职工编号不可重复。 显示所有职工:可以显示所有职工的信息。 删除职工:可以根据职工编号删除职工。 修改职工:可以根据职工编号修改职工信息。 查找职工:可以根据职工编号或者职工姓名查找职工信息。 排序…

    C 2023年5月23日
    00
  • 详解如何利用C++实现一个反射类

    实现一个反射类需要在设计编译时对代码进行注入,故需要使用C++的元编程能力。下面是具体步骤: 1. 定义一个工厂类 反射需要一个通用的工厂类来创建所需类的实例。这个工厂类需要能够被任何需要使用反射类的代码访问。下面是一个通用工程类的示例。 template<typename Base, typename… Args> struct Facto…

    C 2023年5月23日
    00
  • 详解C++中的ANSI与Unicode和UTF8三种字符编码基本原理与相互转换

    下面是详解C++中的ANSI与Unicode和UTF8三种字符编码基本原理与相互转换的攻略。 一、字符编码的概念 字符编码是将字符集中的每个字符映射到某个二进制值的一种方法。常见的字符编码方式包括ASCII、ANSI、Unicode和UTF-8等。 ANSI编码指的是使用单字节表示每个字符的编码方式,它的编码范围是0-127,这种编码方式主要在早期的计算机和…

    C 2023年5月23日
    00
  • C语言模拟掷骰子游戏

    C语言模拟掷骰子游戏攻略 游戏规则 该游戏的规则如下: 玩家选择游戏模式(一次投掷或三次投掷),并输入对应的数字(1或3)。 系统随机生成一个1~6之间的数字,表示掷出的点数。 如果是一次投掷,系统将输出该点数,并提示玩家是否愿意再次投掷。 如果是三次投掷,则继续执行步骤2,直到三次投掷结束。最终输出投掷结果的总和,并提示玩家是否愿意再次投掷。 实现步骤 对…

    C 2023年5月22日
    00
  • JS动态遍历json中所有键值对的方法(不知道属性名的情况)

    下面是完整的攻略。 方法概述 在JavaScript中,我们可以使用for…in语句动态遍历一个json对象中所有的键值对(即属性名和属性值)。但是在不知道这个json对象中的属性名的情况下,如果我们希望能够遍历json对象中所有的键值对,就需要使用一个递归函数来实现。 递归函数原理很简单:对于json对象中的每一个属性值,我们可以判断它的数据类型。如果…

    C 2023年5月23日
    00
  • C语言常见的指针笔试题解析

    C语言常见的指针笔试题解析 什么是指针 在C语言中,指针是指向内存地址的变量。每个变量在内存中都有一个地址,而指针就是存储这个地址的变量。通过指针可以操作内存地址中的内容。 指针的声明和使用 指针的声明使用*来标记,例如: int *p; 这个声明语句表示一个指向整型变量的指针p。如果要让指针p指向某个变量的地址,可以使用&运算符: int a = …

    C 2023年5月23日
    00
  • 一问学会QT时间类

    如何学习QT时间类 一、了解QT时间类 QT时间类是QT框架提供的一个用于处理时间的类,它提供了很多便捷的方法来进行时间计算和转换,并且支持不同的时间格式。其中最常用的时间类有QDateTime、QTime和QDate。 二、基本使用方法 2.1 获取当前时间 使用QDateTime::currentDateTime()函数可以获取当前的时间。 QDateT…

    C 2023年5月23日
    00
  • C语言实现程序开机自启动

    下面我为大家详细讲解如何使用C语言实现程序开机自启动的完整攻略。 1. 注册自启动 Windows 平台 在 Windows 平台上,我们需要在注册表中添加一项,来实现程序开机自启动。具体步骤如下: 打开注册表编辑器,定位到 HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run。 在 …

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部