题解:洛谷 P2911 Bovine Bones

【题目来源】

洛谷:P2911 [USACO08OCT] Bovine Bones G - 洛谷

【题目描述】

Bessie 喜欢桌游和角色扮演游戏,所以她说服了 Farmer John 驾车送她去爱好商店,在那里她购买了三个用于掷骰子的骰子。这些公平的骰子分别有 \(S_1\)\(S_2\)\(S_3\) 个面(\(2 \leq S_1 \leq 20\)\(2 \leq S_2 \leq 20\)\(2 \leq S_3 \leq 40\)),上面分别包含 \(1\)\(S_1\)\(S_2\)\(S_3\) 之间的所有整数。Bessie 不断地掷骰子,试图找出哪个三个骰子的点数和出现得最频繁。如果有多个和出现得最频繁,输出其中最小的和。

【输入】

第 1 行:三个用空格分隔的整数:\(S_1\)\(S_2\)\(S_3\)

【输出】

第 1 行:当骰子以每种可能的组合掷出时,出现次数最多的最小整数和。

【输入样例】

3 2 3

【输出样例】

5

【核心思想】

  1. 问题分析:给定三个骰子的面数 \(S_1, S_2, S_3\),每个骰子掷出 \(1\)\(S_i\) 的整数,求三个骰子点数和出现次数最多的最小值。由于 \(S_i \leq 40\),总组合数最多 \(20 \times 20 \times 40 = 16000\),直接枚举所有组合统计即可。本质上是暴力枚举 + 计数问题。

  2. 算法选择

    • 三重循环枚举:遍历所有 \((i, j, k)\) 组合,\(i \in [1, S_1]\)\(j \in [1, S_2]\)\(k \in [1, S_3]\)
    • 频数统计:用数组 \(a[sum]\) 记录点数和 \(sum = i + j + k\) 的出现次数
  3. 关键步骤

    • 读取数据:读入 \(S_1, S_2, S_3\)
    • 枚举所有组合
      • \(i = 1\)\(S_1\)\(j = 1\)\(S_2\)\(k = 1\)\(S_3\)
      • \(a[i + j + k] \leftarrow a[i + j + k] + 1\)
    • 找最大频数的最小和
      • 初始化 \(max = a[3]\)\(ans = 3\)(最小可能和为 \(3\)
      • 遍历 \(i = 3\)\(S_1 + S_2 + S_3\)
        • \(a[i] > max\),更新 \(max = a[i]\)\(ans = i\)
    • 输出 \(ans\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(S_1 \cdot S_2 \cdot S_3)\),最多 \(16000\) 次枚举
    • 空间复杂度:\(O(S_1 + S_2 + S_3)\),频数数组大小
  5. 暴力枚举的核心思想

    • 数据范围决定算法\(S_3 \leq 40\) 使得总组合数极小,无需任何优化技巧,直接枚举即可
    • 频数数组的索引设计:和的范围为 \([3, S_1+S_2+S_3]\),直接以和值为下标,\(O(1)\) 统计
    • 最小和的处理:遍历从小到大,遇到更大频数才更新,保证相同频数时取最小和
    • 骰子公平性:每个面等概率,枚举即等权重统计,无需额外概率计算
    • 适用于小范围枚举、频数统计、概率分布类问题

【解题思路】

【算法标签】

入门 #模拟

【代码详解】

#include <bits/stdc++.h>  // 包含标准库头文件(万能头文件)
using namespace std;      // 使用标准命名空间

int main()
{
    int s1, s2, s3;      // 定义变量:三个骰子的面数
    int a[85] = {0};     // 定义数组:统计各点数出现的次数(初始化为0)

    cin >> s1 >> s2 >> s3;  // 输入三个骰子的面数

    // 三重循环遍历所有可能的骰子组合
    for (int i = 1; i <= s1; i++)
    {
        for (int j = 1; j <= s2; j++)
        {
            for (int k = 1; k <= s3; k++)
            {
                a[i + j + k]++;  // 统计当前点数出现的次数
            }
        }
    }

    // 初始化最大值和结果(最小可能的点数是3)
    int max = a[3];      // 当前最大出现次数
    int ans = 3;         // 对应的点数

    // 遍历所有可能的点数(从3到s1+s2+s3)
    for (int i = 3; i <= s1 + s2 + s3; i++)
    {
        // 如果当前点数出现次数更多,则更新最大值和结果
        if (a[i] > max)
        {
            max = a[i];  // 更新最大出现次数
            ans = i;     // 更新结果点数
        }
    }

    cout << ans;         // 输出出现次数最多的点数

    return 0;            // 程序正常结束
}

【运行结果】

3 2 3
5
posted @ 2026-08-15 19:48  团爸讲算法  阅读(10)  评论(0)    收藏  举报