题解:洛谷 P5834 MooBuzz

【题目来源】

洛谷:P5834 [USACO19DEC] MooBuzz S - 洛谷

【题目描述】

Farmer John 的奶牛们最近成为了一个简单的数字游戏“FizzBuzz”的狂热玩家。这个游戏的规则很简单:奶牛们站成一圈,依次从一开始报数,每头奶牛在轮到她的时候报一个数。如果一头奶牛将要报的数字是 \(3\) 的倍数,她应当报 Fizz 来代替这个数。如果一头奶牛将要报的数字是 \(5\) 的倍数,她应当报 Buzz 来代替这个数。如果一头奶牛将要报的数字是 \(15\) 的倍数,她应当报 FizzBuzz 来代替这个数。于是这个游戏的开始部分的记录为:

1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16

由于词汇的匮乏,奶牛们玩的 FizzBuzz 中用Moo 代替了 FizzBuzzFizzBuzz。于是奶牛版的游戏的开始部分的记录为:

1, 2, Moo, 4, Moo, Moo, 7, 8, Moo, Moo, 11, Moo, 13, 14, Moo, 16

给定 \(N\),请求出这个游戏中第 \(N\) 个被报的数。

【输入】

输入包含一个整数 \(N\)

【输出】

输出游戏中被报出的第 \(N\) 个数。

【输入样例】

4

【输出样例】

7

【核心思想】

  1. 问题分析:奶牛们依次报数,但跳过所有 \(3\)\(5\) 的倍数(用 Moo 代替)。给定 \(N\),求第 \(N\)实际被报出的数字(即第 \(N\) 个不是 \(3\)\(5\) 倍数的正整数)。本质是一个周期性的数论计数问题。

  2. 算法选择

    • 周期规律法:观察发现每 \(15\) 个数为一个周期(\(3\)\(5\) 的最小公倍数),每个周期内有 \(8\) 个有效数字(非 \(3\) 且非 \(5\) 的倍数),直接利用周期性公式计算
  3. 关键步骤

    • 周期分析:在 \([1, 15]\) 范围内,有效数字为 \(\{1, 2, 4, 7, 8, 11, 13, 14\}\),共 \(8\)
    • 公式推导
      • 完整周期数:\(\lfloor N / 8 \rfloor\),每个完整周期贡献 \(15\) 个数字跨度
      • 剩余位置:\(N \bmod 8\),对应周期内第 \(N \bmod 8\) 个有效数字
      • 答案:\(\text{num} = \lfloor N / 8 \rfloor \times 15 + a[N \bmod 8]\),其中 \(a[0..7] = \{-1, 1, 2, 4, 7, 8, 11, 13, 14\}\)\(a[0] = -1\) 为占位,实际使用 \(a[1..8]\)
    • 直接计算\(O(1)\) 得出结果
  4. 时间/空间复杂度

    • 时间复杂度:\(O(1)\)
    • 空间复杂度:\(O(1)\)
  5. 周期规律法的核心思想

    • 容斥原理确定周期长度\(3\)\(5\) 的最小公倍数为 \(15\),因此有效数字的分布以 \(15\) 为周期重复
    • 周期内计数:每 \(15\) 个数中,\(3\) 的倍数有 \(5\) 个,\(5\) 的倍数有 \(3\) 个,\(15\) 的倍数有 \(1\) 个,有效数字数为 \(15 - 5 - 3 + 1 = 8\)
    • 打表映射余数:将周期内 \(8\) 个有效数字按顺序存入数组,通过余数直接索引,避免实时计算
    • 整数除法拆分:将第 \(N\) 个有效数字拆分为"完整周期贡献"加"周期内偏移",类比时钟的"小时 + 分钟"结构
    • 适用于模周期计数、筛选数列的第 \(N\) 项、FizzBuzz类数论问题

【解题思路】

【算法标签】

普及- #数学

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int n, a[10]={-1,1,2,4,7,8,11,13,14};  // 注意a[0]写为-1
int main()
{
    cin >> n;  // 输入n
    int num = n/8 * 15 + a[n%8];  // 根据数字规律得到此公式
    cout << num << endl;  // 输出结果
    return 0;
}

【运行结果】

4
7
posted @ 2026-08-29 09:38  团爸讲算法  阅读(13)  评论(0)    收藏  举报