top1编程
← 返回题目
题解

约瑟夫问题2

1 条题解

  • 0
    @ 2026-8-6 2:18:11

    P4852 约瑟夫问题2(基础)

    解题思路

    第一步,理解题意。 n 个人围成一圈,编号从 1 到 n,第 i 个人有一个名字。从第 1 个人开始报数,报到 m 的那个人出圈,然后由下一个人重新从 1 开始报数。一直重复,直到所有人都出圈。我们要按出圈的先后顺序,输出每个人的编号和名字。

    第二步,类比游戏。 这就是"丢手绢"游戏:小朋友们围成一圈,从某个小朋友开始往下数,数到 m 的那个小朋友就要走出圈外。数到 m 之后,从下一个小朋友重新数 1、2、3……

    第三步,用数组模拟。 开数组 a,a[i]=0 表示第 i 个人还在圈里,a[i]=1 表示已经出圈。再用三个变量:pos 记录当前报到谁,cnt 记录报数报到几,out 记录已经出圈几个人。名字用二维字符数组 name[i] 存第 i 个人的名字,长度不超过 30。

    第四步,循环报数。 pos 每次向后走一格,走到 n 之后用 pos = pos % n + 1 回到 1,实现"围成一圈"。如果这个人还在圈里,cnt 加 1;当 cnt 等于 m 时,这个人出圈,输出编号和名字,out 加 1,cnt 重新变成 0。重复到 out 等于 n。

    第五步,边界情况。 m 可能比 n 大(比如 n=2、m=1000),程序不关心 m 和 n 的大小关系,只要不断转圈数到 m 为止即可。报数时已经出圈的人要跳过,不能算数。举个例子:n=5、m=3 时,3 号 Xiaotong 先出圈,然后从 4 号开始报数,接下来出圈的是 1 号 Xiaocheng、5 号 Xiaolu、2 号 Xiaomei、4 号 Daxiong,和样例一致。

    参考代码

    // 约瑟夫问题2:n个人围圈报数,数到m出圈,输出编号和名字
    #include <iostream>
    using namespace std;
    int main() {
        int n, m;
        cin >> n >> m;
        char name[1005][35];      // 名字
        for (int i = 1; i <= n; i++) cin >> name[i];
        int a[1005] = {0};        // a[i]=0 表示还没出圈
        int out = 0;              // 已出圈人数
        int pos = 0;              // 当前报数位置
        int cnt = 0;              // 报数
        while (out < n) {
            pos = pos % n + 1;    // 循环移动
            if (a[pos] == 0) {
                cnt++;
                if (cnt == m) {   // 数到m,出圈
                    a[pos] = 1;
                    cout << pos << ' ' << name[pos] << endl;
                    out++;
                    cnt = 0;
                }
            }
        }
        return 0;
    }
    

    复杂度分析

    时间复杂度:每出圈一个人,最多需要转完一整圈 n 个位置,共有 n 个人出圈,所以最坏是 O(n·m) 量级。n、m 最大都是 1000,最多约 1 百万次操作,非常快。

    空间复杂度:名字数组是 n×35 个字符,加上标记数组,是 O(n)。

    • 1