top1编程
← 返回题目
题解

选拔猴王

1 条题解

  • 0
    @ 2026-8-7 12:55:51

    P4837 选拔猴王(入门)

    解题思路

    第一步,理解选大王的过程。 有 N 只猴子要从头到尾排成一队,从第一只开始依次报数1、2、3。报到3的猴子立刻退出队伍;报到1、2的猴子继续留在队伍里。报完队尾以后,又从队伍最前面接着报,继续1、2、3循环。这样一轮一轮下去,队伍里的人越来越少,最后剩下的那一只猴子就是大王。题目要我们输出大王的编号。

    第二步,用队列模拟猴子队伍。 队列正好符合"从头出、从尾进"的特点。我们把猴子按编号1到N依次放进数组 q,用 head 和 tail 两个指针维护队头和队尾。每次从队头取出一只猴子 x,就是执行 x=q[head],然后 head 加1。

    第三步,判断报数决定去留。 用变量 c 记录当前该报到几,它在1、2、3之间循环。取出猴子 x 后:如果 c 等于3,这只猴子报到3,退出队伍,同时把 c 重新变成1;如果 c 是1或2,这只猴子不退出,而是转到队尾继续排队,即 q[tail++]=x,同时 c 加1。这样每处理一只猴子,报数就前进一步。

    第四步,什么时候结束。 当队列里只剩一只猴子时(head+1 等于 tail),停止循环,队列里这只猴子就是大王,输出它即可。比如 N=4:1报到1转到队尾,2报到2转到队尾,3报到3退出;4报到1转到队尾,1报到2转到队尾,2报到3退出;4报到1转到队尾,1报到2转到队尾,4报到3退出,最后剩下1,输出1,和样例一致。

    第五步,注意数组大小。 因为报到1、2的猴子会反复回到队尾,队列会比一开始长不少,最多需要大约3N个格子。N最大是100,所以数组开400个格子就足够了。

    参考代码

    // 选拔猴王:约瑟夫问题,1-3循环报数,报到3的退出,求最后留下的猴子
    #include <iostream>
    using namespace std;
    
    int main() {
        int q[400];
        int n, i;
        cin >> n;
        for (i = 0; i < n; i++) q[i] = i + 1;  // 猴子编号1~n依次入队
        int head = 0, tail = n;
        int c = 1;  // 当前报到的数,1、2、3循环
        while (head + 1 < tail) {   // 至少还剩2只猴子
            int x = q[head++];
            if (c == 3) {           // 报到3的猴子退出
                c = 1;
            } else {                // 报1或2的猴子转到队尾
                q[tail++] = x;
                c++;
            }
        }
        cout << q[head] << endl;    // 最后剩下的猴子就是大王
        return 0;
    }
    

    复杂度分析

    N 最大为100,每只猴子在队列中进进出出,总共大约3N次操作,所以时间复杂度是 O(N)。空间上数组 q 最多需要3N个格子,空间复杂度是 O(N)。

    • 1