当前位置: 首页 > news >正文

P1996 约瑟夫问题

题目链接:P1996 约瑟夫问题 - 洛谷
题目难度:普及-


目录

解法 1:队列模拟(STL queue)

解法 2:数组模拟

解法 3:链表模拟(单链表)

解法 4:递归公式

解法 5:迭代公式


解法 1:队列模拟(STL queue)

#include <iostream>
#include <queue>
using namespace std;int main() {int n, m;cin >> n >> m;queue<int> q;for (int i = 1; i <= n; ++i) q.push(i);while (!q.empty()) {for (int i = 1; i < m; ++i) { // 前m-1个移到队尾q.push(q.front());q.pop();}cout << q.front() << " "; // 第m个出队q.pop();}return 0;
}

讲解:

该方法利用 STL 队列的 FIFO 特性直观模拟约瑟夫环过程。先将 1 到 n 依次入队,形成初始的环形序列。每次循环时,为定位第 m 个待淘汰元素,需把前 m-1 个元素逐个弹出并重新压入队尾 —— 这一步完美复现了 “环” 的循环特性,即跳过前 m-1 个元素后,队首便是第 m 个目标。将目标元素弹出并输出后,重复操作直至队列清空。整个过程与问题描述高度一致,无需复杂逻辑转换,每步操作都对应着实际淘汰流程,适合新手通过可视化步骤理解约瑟夫环的核心逻辑。

解法 2:数组模拟

#include <iostream>
using namespace std;int main() {int n, m, cnt = 0, pos = 0;cin >> n >> m;bool vis[105] = {false};for (int i = 0; i < n; ++i) {int step = 0;while (step < m) { // 找第m个未出圈的pos = (pos + 1) % n;if (!vis[pos]) step++;}vis[pos] = true;cout << pos + 1 << " ";}return 0;
}

讲解:

此方法用布尔数组标记元素是否已出圈,通过位置偏移模拟环的循环。变量 pos 记录当前位置,每次循环需移动 m 步(遇到已出圈元素则跳过),确保找到第 m 个未出圈的元素。找到后标记该位置为已出圈并输出(注意转换为 1 基编号)。相比队列,数组无需频繁移动元素,仅通过标记和位置计算实现模拟,空间复杂度更低,且避免了队列操作的额外开销,适合理解 “标记 - 查找” 的高效模拟思路。

解法 3:链表模拟(单链表)

#include <iostream>
using namespace std;struct Node {int val;Node* next;Node(int x) : val(x), next(nullptr) {}
};int main() {int n, m;cin >> n >> m;Node* head = new Node(1);Node* cur = head;for (int i = 2; i <= n; ++i) { // 建环cur->next = new Node(i);cur = cur->next;}cur->next = head;while (n--) {for (int i = 1; i < m; ++i) cur = cur->next; // 移到第m-1个cout << cur->next->val << " ";Node* del = cur->next;cur->next = del->next; // 删除第m个delete del;}return 0;
}

讲解:

通过构建循环单链表模拟环形结构,每个节点存储一个数字,尾节点指向头节点形成闭环。每次淘汰第 m 个元素时,先将指针移动到该元素的前一个节点,再通过修改指针删除目标节点并输出其值。链表的动态删除特性恰好匹配约瑟夫环中 “元素逐个移除” 的需求,无需像数组那样预留空间或处理标记,能直观体现环的连续性和元素移除后的结构变化,适合理解链表在动态序列操作中的优势。

解法 4:递归公式

#include <iostream>
using namespace std;int f(int n, int m) {return n == 0 ? 0 : (f(n - 1, m) + m) % n;
}int main() {int n, m;cin >> n >> m;for (int i = 1; i <= n; ++i) { // 依次求每个出圈位置int pos = f(i, m) + 1; // 转换为1基cout << pos << " ";}return 0;
}

讲解:

基于约瑟夫环的数学递推关系:f (n,m) 表示 n 个元素时最后剩下的位置,其值为 (f (n-1,m)+m)% n(n=0 时为 0)。递归过程从 n=1 逐步向上计算,每次利用 n-1 规模的结果推导 n 规模的解。该方法跳出了模拟的思维框架,通过数学归纳将复杂问题分解为更小的子问题,时间复杂度降至 O (n),无需存储整个序列,仅通过函数调用栈传递中间结果,适合理解递归思想在数学建模中的应用。

解法 5:迭代公式

#include <iostream>
using namespace std;int main() {int n, m;cin >> n >> m;int* res = new int[n + 1];res[0] = 0;for (int i = 1; i <= n; ++i) { // 迭代计算res[i] = (res[i - 1] + m) % i;}for (int i = 1; i <= n; ++i) {cout << res[i] + 1 << " ";}delete[] res;return 0;
}

讲解:

将递归公式转化为迭代计算,用数组存储每个规模 i(1≤i≤n)的解。从 i=1 开始,依次计算 res [i]=(res [i-1]+m)% i,最后将结果转换为 1 基编号输出。迭代避免了递归可能的栈溢出问题,计算过程更可控,且数组存储的中间结果可直接复用,无需重复计算。这种方式兼顾了数学公式的高效性和迭代的稳定性,适合理解如何将递归逻辑转化为更实用的循环实现,尤其适合 n 较大的场景。。


笔者博客:jdlxx_dongfangxing-CSDN博客

http://www.dtcms.com/a/446614.html

相关文章:

  • 有哪些学做衣服的网站网站开发团队人数构成
  • 做网站苏州淘宝店网站建设
  • 对面试的一些思考
  • 【代码随想录day 35】 力扣 01背包问题 二维
  • 百度网盘怎么做网站友情链接交换平台免费
  • 网站模版亮点网站建设有关表格
  • 手机端网站制作教程合肥大型网站制
  • 鞍山高新区网站软文技巧
  • wordpress做物流网站网站建设合同书相关附件
  • 软件供应链风险预测实操指南——从SCA到SBOM 2.0的全流程落地
  • 免费网站排名优化在线产品营销推广方案
  • C语言-指针总结
  • 非小米电脑安装电脑管家实现互联互通
  • 怎样在网站上做外贸php网站开发环境说明
  • PostgreSQL 单库备份
  • 阳春做网站已有网站开发app客户端
  • 版面设计图大全长沙如何优化排名
  • 网站验证码文件厦门建设银行网站
  • 力扣hot100做题整理(31-40)
  • Oracle OCP认证考试题目详解082系列第54题
  • 淘宝客网站搜索怎么做湖南优化电商服务有限公司
  • 安阳市建设工程领域网站图片 移动网站开发
  • 掌握 Mock 的艺术:用 unittest.mock 优雅隔离外部依赖的单元测试实战指南
  • 哪些网站可以做兼职设计西安建设和住房保障局网站
  • 松江佘山网站建设南县网站设计
  • 网站备案号密码找回天水市住房和城乡建设局网站
  • php 8.4.6 更新日志
  • Linux处理停止信号相关函数的实现
  • 【学习笔记】Redis数据库设计与实现研究综述
  • 一键整合,万用万灵,Python3.11项目嵌入式一键整合包的制作(Embed)