题解——6. 小E的怪物挑战 | 豆包MarsCode AI刷题

175 阅读3分钟

原题截图

image.png

题意分析

原题题意分析

nn个怪物,每个怪物都有血量hih_i和攻击力aia_i,玩家初始血量为HH,攻击力为AA

  1. 玩家击败的第一个怪物的血量和攻击力必须都低于自己。
  2. 击败怪物后,玩家的血量为HH和攻击力为AA会变成他击败的那个怪物的血量hih_i和攻击力aia_i
  3. 从击败的第22个怪物开始,击败的第ii个怪物的血量和攻击力必须分别大于击败的第i1i-1个怪物的血量和攻击力,即设击败的第ii个怪物的血量为whiwh_i,攻击力为waiwa_i,则对于所有的2in2\le i\le n,有whi1<whiwh_{i-1}<wh_iwai1<waiwa_{i-1}<wa_i

问玩家最多可以击败多少个怪物。

题意补充

本题题干有一个非常大的漏洞:没有说明击败怪物的顺序。按照最常见的两种题理解——“必须从左到右击败”以及“可以按任意顺序击败”分别编写代码后,可以发现样例数据均无法通过。通过翻阅掘金平台上其他同学发布的文章,才知道本题的题意击败怪物顺序的要求是:必须从右到左击败。

解题思路

本题和一个经典的问题——最长上升子序列非常相似。最长上升子序列的解法为:设ans[i]为从1到i的子数组的最长上升子序列,n为数组长度,从1到n遍历数组,设遍历到的元素为i,对于每个i,从0到i遍历数组,设遍历到的元素为j,如果j<i,则i可以接在子数组[1,j]的最长上升子序列的后面,此时ans[i] = max(ans[i],ans[j]+1)。遍历完成后,取ans[n]的值即为答案。

而本题与最长上升子序列问题的不同是,本题中“i可以接在子数组[1,j]的最长上升子序列”的条件不是i<j,而是hi1<hih_{i-1}<h_iai1<aia_{i-1}<a_i,所以将最长上升子序列的代码中的i<j替换为whi1<whiwh_{i-1}<wh_iwai1<waiwa_{i-1}<wa_i,即可得出本题的代码。

最后,由于本题规定击败怪物的顺序是必须从右到左击败。所以在遍历开始之前需要执行一遍reverse(h.begin(),h.end()); reverse(a.begin(),a.end());

代码实现

#include <bits/stdc++.h>
using namespace std;
constexpr int inf=INT_MAX/2;
int solution(int n, int H, int A, vector<int> h, vector<int> a)
{
    // To官方:“击败顺序是从右到左,为什么不在题干里说清楚?”
    reverse(h.begin(),h.end());
    reverse(a.begin(),a.end());
    vector<int> ans(n,-inf);
    for(int i=0;i<n;i++) if(h[i]<H && a[i]<A) ans[i] = 1;
    int xmax=0;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<i;j++)
        {
            if(h[j]>h[i] && a[j]>a[i])
            {
                ans[i] = max(ans[i],ans[j]+1);
            }
        }
        xmax = max(xmax,ans[i]);
    }
    return xmax;
}

总结

本题是最长上升子序列问题的变种,因此使用的是动态规划算法,设怪物个数为n,则本题时间复杂度为O(n2)O(n^2),空间复杂度为O(n)O(n)。如果你有复杂度更优的解法,欢迎在下方评论。