求助|黑塔转圈圈问题
29770
2023.08.14
2023.08.15
发布于 未知归属地

image.png

题目

黑塔女士的普通攻击可以对一名敌人造成一点伤害。此外,黑塔女士的天赋,会在敌人的生命值首次降低到初始值的二分之一(向下取整)时,立即发动追加攻击,对所有敌人造成一点伤害。

追加攻击造成的伤害可以继续触发追加攻击,且多个敌人的生命值同时下降到二分之一时,每个敌人都可以触发一次追加攻击。

现在有许多敌人,黑塔女士应该如何用最少的普通攻击消灭所有敌人呢?

数据格式

  • 输入
    一个长度为n整数数组a, 表示有n名敌人,第i名敌人初始生命值为a[i]
  • 输出
    一个整数,表示消灭所有敌人最少需要的普通攻击次数。

示例

[5, 6]
7    // 需要普通攻击0号敌人3次,1号敌人4次。
[1, 2, 3, 4, 5]
1    // 对0号敌人或者对1号敌人进行一次普通攻击即可。
[1, 3, 6]
3    // 对初始生命为6的敌人进行3次普通攻击即可。

个人思路

想到了一种贪心的思路,将数组从小到大排序后,依次攻击,每个都攻击到触发为止,攻击完一轮,所有可能的追加攻击都触发过了,再遍历一轮补刀。但这个思路不对,示例中的第3组数据就过不了。
求大佬解答。

8.15更新

谢谢各位大佬积极讨论。目前看到比较多的思路是先把超过n的敌人的生命值削减到n,然后依次攻击距离最接近半血的敌人。整理了一下,得到代码如下:

long long minimumNormalAttack(vector<int> nums)
{
    long long normalAttackCount = 0;
    int n = nums.size();

    vector<pair<int, int>> enemies;

    for (int i = 0; i < n; i++)
    {
        enemies.push_back(make_pair(nums[i], nums[i])); // 每个pair的first是当前血量,second是初始血量
    }

    int aoeCount = 0; // 统计已经触发过AOE的次数

    for (int i = 0; i < n; ++i)
    {
        auto &enemy = enemies[i];
        if (enemy.second > n) // 如果敌人的初始血量大于n, 这些超过n的血量值早晚要用普攻打, 所以先进行这些普攻, 有利于更早触发AOE
        {
            normalAttackCount += enemy.second - n;
            enemy.first = n;

            if (n <= enemy.second / 2)
            {
                aoeCount++;
            }
        }
    }

    auto compare = [](pair<int, int> a, pair<int, int> b)
    {
        // 比较哪个敌人的当前血量更接近初始值的一半, 假设传入参数不会有血量小于初始值一半的情况
        return a.first - a.second / 2 > b.first - b.second / 2;
    };

    // 其实在vector里直接排序也可以, 但是compare就要考虑到血量小于初始值一半的情况
    priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(compare)> pq(compare);
    for (int i = 0; i < n; ++i)
    {
        if (enemies[i].first > enemies[i].second / 2)
        {
            pq.push(enemies[i]);
        }
    }

    while (!pq.empty())
    {
        // 每次取出当前血量最接近初始值一半的敌人
        auto enemyNearestToHalf = pq.top();
        pq.pop();

        // 考虑已经触发过的AOE能否把这个敌人的血量打到初始值一半以下, 如果不能, 就用普攻打到触发AOE为止
        if (enemyNearestToHalf.first - aoeCount > enemyNearestToHalf.second / 2)
        {
            normalAttackCount += enemyNearestToHalf.first - enemyNearestToHalf.second / 2 - aoeCount;
        }
        ++aoeCount;

        // 剩余多少血就不用管了, 只要所有AOE都触发一遍, 剩余的血量都会被打掉
    }
    return normalAttackCount;
}

当然我也没有标准答案或者判题程序,无法验证各个思路的正确性。欢迎大家继续讨论。

评论 (62)