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

201 阅读3分钟

问题描述

小E在一个游戏中遇到了 nn 个按顺序出现的怪物,每个怪物都有其特定的血量 hih_i 和攻击力 aia_i。小E的初始血量为 HH,攻击力为 AA。 游戏规则如下:

  1. 小E可以击败一个血量和攻击力都小于她当前属性的怪物。
  2. 对于第一个击败的怪物,需要满足其血量小于 HH 且攻击力小于 AA
  3. 击败怪物后,小E会获得该怪物的属性值。
  4. 为了保持战斗节奏,要求击败的怪物序列中,后一个怪物的血量和攻击力都必须严格大于前一个怪物。
    小E想知道,她最多能击败多少怪物。

思路历程

首先要注意,这道题目疑似是有极大问题的,一个是数据可能极弱,一个是题意非常之不明确。

初步设想

通过对多篇题解的分析,我认为这篇题解是接近真正解的6.小E的怪物挑战 )

动态规划数组就是:

我们用 dp[i] 表示以第 ii 个可击败怪物结尾的最长递增子序列的长度。
初始化每个 dp[i] 的值都为 1,因为每个怪物至少可以单独成为一个子序列。

状态转移就是:

  • 对于每一个怪物 ii,遍历所有之前的怪物 jjj < i),如果怪物 jj 的血量和攻击力都小于怪物 ii 的血量和攻击力(即 h[j] < h[i]a[j] < a[i]),那么我们可以将怪物 ii 添加到以怪物 jj 结尾的子序列中。
  • 更新 dp[i] 的值为 dp[j] + 1,表示在以 jj 结尾的子序列基础上增加怪物 ii
  • 取所有可能的 dp[j] + 1 中的最大值,赋给 dp[i]
  • 那么最终答案就是每一个子序列长度的最大值

但是这篇题解依托于一个条件:从左打怪打到右,即数据已经有序,这是非常致命的一个条件,因为我们很容易构造一个 hack:把原本能打到的一个怪放在第一个,根据这些默认从左往右打怪的伪正解,第一个怪是无法被加入最长公共子序列的。

尽管以上思路是伪正解,但是出于题目数据弱,可以通过。

深入讨论

发现问题了吗?

对啊, LIS 要保证序列自身是单调的啊,不然你单调个毛线最长递增子序列?

如何排序?我们知道在单一变量贪心问题上,直接单关键字排序即可,又或者在《国王游戏》这道极其经典的双变量贪心问题上,通过讨论得到一个新的单一关键字进行排序。但是由于此题的特殊条件,我们恐怕很难找到一个单一关键字可以实现这样的排序。其实,直接双关键字排序,正确性是有保障的:

image.png

对于另一个众多题解忽略的问题:攻击力 H 和血量 A 的变化问题,用 h,a 数组分别表示以 i 结尾时,小E的数值大小,转移时加上一层比较即可。

如此,我们便讨论出了真正的正解