2 条题解

  • 0
    @ 2026-1-3 15:49:17

    我不行了,怎么又是博弈论......

    啊!

    啊!

    啊!

    啊!

    啊!

    本蒟蒻只能浅浅拿捏了 —————————————————————————————————————————————— 题目分析 核心问题:你和 Slavik 在玩一个回合制游戏。(谁爱玩谁玩去,再也不玩了~) 你的回合:选择一扇门,使其耐久度减少 x(最低减到 0)。 Slavik 的回合:选择一扇非零耐久度的门,使其耐久度增加 y。 目标: 你希望游戏结束时,耐久度为 0 的门的数量最大化。 Slavik 希望这个数量最小化。 问题:在双方都采取最优策略的情况下,最终会有多少扇门的耐久度为 0? 思路分析 要解决这个问题,我们需要将其分解为对每一扇门的独立分析,然后综合所有门的结果。

    1. 分析单扇门的博弈 对于一扇初始耐久度为 a_i 的门,我们需要判断在你和 Slavik 的最优策略下,它最终是否会被破坏(耐久度为 0)。 我们可以分两种情况讨论: 情况 A:你的攻击力 x 大于等于 Slavik 的修理力 y (x >= y) 你的优势:你的破坏力至少和他的修复力相当。 你的策略:你可以一直攻击同一扇门。每次你的回合,你打掉 x 点耐久。如果 Slavik 选择修理这扇门,他在他的回合会增加 y 点耐久。一个完整的回合(你和他各行动一次)下来,这扇门的耐久度净减少 x - y。 Slavik 的策略:修理这扇门是徒劳的,因为耐久度总会缓慢下降。他最好的策略是放弃修理这扇门,去修理其他门。 结果:既然 Slavik 放弃修理,你只需要 ceil(a_i / x) 次攻击就能将这扇门的耐久度降到 0。一旦门被破坏,Slavik 就再也无法修理它了。 结论:在 x >= y 的情况下,你可以破坏任何一扇门。 情况 B:你的攻击力 x 小于 Slavik 的修理力 y (x < y) Slavik 的优势:他的修复力比你的破坏力强。 你的策略:你必须在 Slavik 来得及修理之前就把这扇门破坏掉。你只有一次攻击机会(你的第一个回合)。如果你不能在这次攻击中把它打坏,那么 Slavik 就可以在他的回合把它修好,并且他的修理量 y 大于你的下一次攻击量 x,门的耐久度会越来越高,你将永远无法破坏它。 Slavik 的策略:他会修理那些你无法在一次攻击中破坏的门,以防止你未来将它们破坏。 结果:你只有一次机会。你能否破坏这扇门,完全取决于你的攻击力 x 是否大于等于它的初始耐久度 a_i。 结论:在 x < y 的情况下,你只能破坏那些初始耐久度 a_i <= x 的门。
    2. 综合所有门的策略 现在我们知道了如何判断一扇门能否被破坏。接下来是如何在所有门中选择目标,以实现最终破坏数量的最大化。 如果 x > y:你可以破坏所有门。所以最终答案就是 n。 如果 x <= y: 你的策略:为了最大化破坏数量,你应该优先攻击那些最容易破坏的门。也就是初始耐久度 a_i 最小的那些门。 Slavik 的策略:为了最小化被破坏的数量,他会优先修理那些最容易被你破坏的门。也就是初始耐久度 a_i 最小的那些门。 这就形成了一个经典的 “配对” 博弈。 你会从耐久度最低的门开始攻击。 Slavik 会从耐久度最低的门开始修理。 博弈过程: 你选择一扇门攻击。如果 a_i <= x,你成功破坏它。 Slavik 选择一扇门修理。他会选择剩下的门中耐久度最低的。如果这扇门的 a_i <= x,他会修理它,阻止你下一次破坏它。 这个过程会持续下去,直到所有 a_i <= x 的门都被你破坏或者被 Slavik 修理。 最终能破坏的门的数量,就取决于你和 Slavik 行动的 “节奏”。 你先行动。 你破坏一扇 a_i <= x 的门。 Slavik 修理一扇 a_i <= x 的门。 你再破坏一扇... Slavik 再修理一扇... 这个节奏意味着,你破坏的门和 Slavik 修理的门是交替出现的。 我们可以用一个数学公式来表达最终被破坏的门的数量: 首先,统计有多少扇门是 “可破坏的”,即满足 a_i <= x。设这个数量为 k。 由于你先动手,你总能比 Slavik 多破坏一扇门(如果 k 不为 0)。 所以,最终被破坏的门的数量就是 ceil(k / 2)。在整数运算中,这等价于 (k + 1) / 2。 算法设计 读取输入:读取门的数量 n,你的攻击力 x,Slavik 的修理力 y,以及所有门的初始耐久度数组 a。 判断攻击力优势: 如果 x > y:你可以破坏所有门。直接输出 n。 如果 x <= y: 遍历数组 a,统计其中满足 a_i <= x 的门的数量,记为 k。 最终能破坏的门的数量为 (k + 1) / 2。输出这个结果。 C++ 代码实现:
    #include <iostream>
    #include <vector>
    
    int main() {
        // 提高C++ I/O效率
        std::ios_base::sync_with_stdio(false);
        std::cin.tie(NULL);
    
        int n, x, y;
        std::cin >> n >> x >> y;
    
        std::vector<int> a(n);
        for (int i = 0; i < n; ++i) {
            std::cin >> a[i];
        }
    
        // 如果我的攻击力大于Slavik的修理力,我可以破坏所有门
        if (x > y) {
            std::cout << n << "\n";
        } else {
            // 否则,我只能破坏那些初始耐久度 a_i <= x 的门
            // 统计这样的门的数量
            int count = 0;
            for (int durability : a) {
                if (durability <= x) {
                    count++;
                }
            }
            // 在我和Slavik的交替博弈下,我能破坏其中的 (count + 1) / 2 扇
            // 这是因为我先手,总能比他多破坏一个(如果count > 0)
            std::cout << (count + 1) / 2 << "\n";
        }
    
        return 0;
    }
    

    代码解释 #include <...>:包含了标准输入输出流和动态数组 vector 所需的头文件。 std::ios_base::sync_with_stdio(false); std::cin.tie(NULL);:这是竞赛编程中常用的优化,用于加速 cin 和 cout。 读取数据:代码首先读取 n, x, y,然后读取 n 个门的耐久度到 vector a 中。 核心逻辑判断: if (x > y):这是最关键的判断。如果你的攻击力 x 严格大于 Slavik 的修理力 y,你就掌握了绝对优势,可以破坏所有门。 else 块处理 x <= y 的情况。 在 else 块中,我们遍历数组 a,用一个计数器 count 统计有多少扇门的初始耐久度 a_i 小于等于你的攻击力 x。 std::cout << (count + 1) / 2 << "\n";:根据我们推导出的公式,计算并输出最终能破坏的门的数量。这里的整数除法 (count + 1) / 2 完美地实现了向上取整的效果。 这个算法的时间复杂度是 O(n),空间复杂度是 O(n),完全可以满足题目 n ≤ 100 的要求。

    信息

    ID
    46
    时间
    1000ms
    内存
    256MiB
    难度
    5
    标签
    (无)
    递交数
    21
    已通过
    12
    上传者