- C++
约瑟夫问题详解(C++ 实现
- 2025-7-3 22:07:38 @
约瑟夫问题详解(C++ 实现,适合中小学生 0 基础学习)
约瑟夫问题是一个经典的趣味数学问题,也常作为编程入门案例。简单来说,就是一群人围成圈,从某个人开始报数,报到特定数字的人出局,接着从下一个人重新开始报数,直到最后剩下一人。下面用通俗易懂的方式,带大家用 C++ 代码实现它,还会配上详细注释,方便中小学生理解。
一、问题简化描述(讲故事版)
想象一下,咱们班同学围成一个圈做游戏。老师说“从第 1 个同学开始,每次数到 3 的同学要出列,然后接着从下一个同学重新数 1、2、3……” ,最后看看谁能留到最后。这就是约瑟夫问题的简单场景,咱们用代码模拟这个过程,找出最后留下的同学编号 。
二、解题思路(分步理解)
- 模拟围成圈:可以用数组或者更方便的
vector
(一种 C++ 里的容器,像可变长度的数组)来表示参与的人,把每个人的状态标记为 “在圈里(比如用 1 表示)” 或者 “已出局(用 0 表示)” 。 - 报数与出局:从当前位置开始,逐个往后数,数到目标数字(比如 3 )时,把对应同学标记为出局(状态改成 0 )。然后从出局同学的下一个位置,重新开始新一轮报数。
- 循环直到剩一人:不断重复 “报数 - 出局” 的过程,直到圈里只剩下一个状态为 1(在圈里)的同学,这个同学就是最后留下的。
三、C++ 代码实现(超详细注释版)
#include <iostream>
// 引入 vector 容器,方便处理动态数组
#include <vector>
using namespace std;
int main() {
// 第一步:输入参与人数和报数出局的数字
int n, m;
cout << "请输入参与游戏的总人数 n:";
cin >> n;
cout << "请输入每次数到几出局(比如 3 ):";
cin >> m;
// 用 vector 表示每个人的状态,初始都在圈里,用 1 标记
vector<int> people(n, 1);
// 当前报数的位置,从第 0 个人开始(电脑里计数常从 0 开始,对应实际第 1 个同学)
int index = 0;
// 记录当前圈里剩下的人数,初始是 n 人
int count = n;
// 记录当前报数到几了
int num = 0;
// 第二步:循环,直到圈里只剩 1 人
while (count > 1) {
// 如果当前同学在圈里(状态是 1 ),才参与报数
if (people[index] == 1) {
num++;
// 数到 m 了,该同学出局
if (num == m) {
people[index] = 0;
count--;
num = 0;
}
}
// 移动到下一个同学位置,到最后一个后,回到开头(模拟围成圈)
index = (index + 1) % n;
}
// 第三步:找出最后留下的同学编号(实际编号要 +1,因为电脑从 0 计数)
for (int i = 0; i < n; i++) {
if (people[i] == 1) {
cout << "最后留下的同学是原来的第 " << i + 1 << " 号" << endl;
}
}
return 0;
}
四、代码逐行解释(适合 0 基础理解)
- 头文件引入:
#include <iostream>
#include <vector>
using namespace std;
#include <iostream>
:让程序能实现输入输出,比如cout
输出内容到屏幕,cin
从键盘读入内容 。#include <vector>
:引入vector
容器,它就像一个可以自动调整大小的数组,方便咱们处理 “一群人” 的状态 。using namespace std;
:避免每次用cout
、vector
等时,都要写std::
,让代码更简洁 。
- 输入部分:
int n, m;
cout << "请输入参与游戏的总人数 n:";
cin >> n;
cout << "请输入每次数到几出局(比如 3 ):";
cin >> m;
- 定义
n
存储总人数,m
存储每次数到几出局 。 - 用
cout
输出提示文字,让你输入对应数字,再用cin
把输入的数字存到n
和m
里 。
- 初始化参与人员状态:
vector<int> people(n, 1);
- 创建一个叫
people
的vector
,里面有n
个元素,每个元素初始值是1
。1
表示这个同学在圈里,后续出局了会改成0
。
- 关键变量初始化:
int index = 0;
int count = n;
int num = 0;
index
:记录当前报数到哪个人了,初始从第 0 个位置开始(对应实际第一个同学)。count
:记录圈里还剩下多少人,初始是n
人 。num
:记录当前报数的数字,初始从 0 开始,每次遇到在圈里的同学就加 1 。
- 核心循环(模拟报数出局过程):
while (count > 1) {
if (people[index] == 1) {
num++;
if (num == m) {
people[index] = 0;
count--;
num = 0;
}
}
index = (index + 1) % n;
}
while (count > 1)
:只要圈里剩下的人多于 1 个,就继续循环报数、出局 。if (people[index] == 1)
:判断当前位置的同学是否在圈里(状态是 1 ),在的话才参与报数 。num++
:报数加 1 。if (num == m)
:如果数到了要出局的数字m
:people[index] = 0
:把当前同学状态改成 0 ,表示出局 。count--
:圈里剩下的人数减 1 。num = 0
:重置报数,下一轮从 1 开始数 。
index = (index + 1) % n;
:移动到下一个同学位置,% n
是为了到最后一个位置后,能回到开头,模拟围成圈的效果 。
- 找出并输出最后留下的同学:
for (int i = 0; i < n; i++) {
if (people[i] == 1) {
cout << "最后留下的同学是原来的第 " << i + 1 << " 号" << endl;
}
}
- 用
for
循环遍历people
,找到状态还是1
的同学(就是最后留下的)。 - 因为电脑里
i
从 0 开始计数,实际同学编号要i + 1
,所以输出时加上 1 ,符合咱们日常从 1 开始数的习惯 。
五、运行示例(手把手教你用)
- 输入:
假设输入总人数
n = 5
,每次数到m = 3
出局 。输入如下:
请输入参与游戏的总人数 n:5
请输入每次数到几出局(比如 3 ):3
- 过程模拟:
- 初始
people = [1,1,1,1,1]
,index = 0
,count = 5
,num = 0
。 - 第一轮报数,数到第 3 个人(索引 2 )时,他出局,
people
变成[1,1,0,1,1]
,count = 4
。接着从索引 3 开始新一轮报数…… 不断循环。
- 输出结果:
最后会输出类似
最后留下的同学是原来的第 4 号
(实际结果大家运行代码就能看到,每次可能因输入不同有变化 )。
六、拓展与思考(加深理解)
- 改变参数:可以试试输入不同的
n
(比如 10 个人)和m
(比如数到 4 出局 ),看看最后留下的人有啥变化,感受规律 。 - 优化思考:这个代码是用最直观的方式模拟过程,适合理解。其实还有更高效的数学方法(比如递推公式)解决约瑟夫问题,等大家学了更多知识,也可以尝试探索 。
这样,从问题故事化描述,到思路拆分,再到代码逐行解释,相信中小学生朋友们能一步步理解约瑟夫问题的 C++ 实现啦,快去运行代码试试吧 !
0 条评论
目前还没有评论...