这是一个很好的动态规划问题。
思考动态规划的方法是问自己两个问题:
- 如果我将一个(或多个)变量减少到零(或类似),是否存在此问题的简单版本?
- 如果我知道所有
n 大小问题的答案,是否有一种简单的方法来计算n+1 大小问题的答案? (这里,“大小”是针对特定问题的,您需要找到有助于解决手头问题的正确大小概念。)
对于这个问题,什么是简单的版本?好吧,假设天数是 1。那么很容易:我停止机器,尽可能多地治疗患者。做其他事情没有意义。
现在,如果我们将剩余天数视为大小的概念,我们也会得到第二个问题的答案。假设我们知道剩下n 天的所有问题的所有答案。如果还剩days 天,并且机器最初运行了running 天,我们可以写maxTreat(days, running) 来表示我们可以处理的最大数量。
现在还有n+1 天;到目前为止,这台机器已经运行了k 天。我们有两个选择:(1)停止机器; (2) 不要停止。如果我们停止机器,我们今天可以治疗一些患者(我们可以根据k 计算出数量),然后我们可以治疗maxTreat(n, 1) 患者,因为那时还有n 天,到明天机器将再次运行仅一天。如果我们不停止机器,我们今天就无法治疗任何人,但此后我们将能够治疗maxTreat(n,k+1) 患者,因为到明天机器将运行k+1 天。
我会让你计算出精确的细节,但为了有效地解决它,我们根据剩余的天数和机器到目前为止运行的天数创建一个多维数组。然后我们遍历数组,解决所有可能的问题,从琐碎的(还剩一天)开始,向后工作(两天,然后是三天,依此类推)。在每个阶段,我们要解决的问题要么是微不足道的(因此我们可以将答案写进去),要么可以根据我们在上一步写入数组的条目进行计算。
动态编程真正酷的地方在于,我们会在执行过程中创建所有结果的缓存。因此,对于递归方法最终需要多次计算子问题的答案的问题,使用动态规划,我们永远不会不止一次地解决子问题。
现在我已经看到了您的实现,还有其他 cmets:
首先,当你达到 10,000 左右时它开始减速,我并不感到惊讶。该算法是O(n^2),因为在每次迭代中,您必须在数组中填充最多n 个条目,然后才能进入下一个级别。不过,我很确定 O(n^2) 是您将在这个谜题中获得的最佳渐近复杂度。
如果您想进一步加快速度,可以考虑采用自上而下的方法。目前你正在做自下而上的动态规划:解决大小为 0 的情况,然后是大小为 1 的情况,依此类推。但你也可以反过来做。本质上,这与您编写一个效率极低的递归解决方案的算法相同,该解决方案可以即时计算子问题的解决方案,只是每次计算时都会缓存一个结果。所以它看起来像这样:
- 设置二维数组以保存子问题的解决方案。为每种情况预先填充 -1。值 -1 表示您尚未解决该子问题。
- 编写一个解决
maxTreat(days, running) 的例程,以解决下一级子问题的答案。当您想要子问题的答案时,请查看数组。如果那里有一个-1,那么你还没有解决那个问题,所以你递归地解决它,然后将答案放入数组中。如果有除 -1 以外的任何值,您可以使用在那里找到的值,因为您已经计算过了。 (您也可以使用HashMap 代替多维数组。)
这一方面更好,另一方面更糟。更糟糕的是,您有与递归相关的开销,并且因为递归调用最终会耗尽堆栈。您可能需要使用 JVM 的命令行参数来增加堆栈大小。
但在一个关键方面更好:你不计算所有子问题的答案,而只计算你需要知道答案的那些。对于某些问题,这是一个巨大的差异,而对于某些问题,则不是。很难获得正确的直觉,但我认为这可能会产生很大的不同。毕竟,每个答案只取决于前一行的两个子问题。
最终的解决方案(在您首先获得自顶向下递归之前不要尝试此方法!)是自顶向下但不使用递归。这将避免堆栈空间问题。为此,您创建了一堆需要解决的子问题(使用ArrayDeque),并不断将它们从队列的前面移走,直到没有剩余。第一件事是将需要解决的大问题推入堆栈。现在,您迭代地从堆栈中弹出问题,直到它为空。弹出一个,并称之为P。那么:
- 查看您的数组或
HashMap 以查看P 是否已解决。如果是,请返回答案。
- 如果没有,请查看
P 的子问题是否已经解决。如果他们有,那么您可以解决P,并缓存答案。如果堆栈现在是空的,那么您已经解决了最后一个问题,并且您输出了P 的答案。
- 如果子问题还没有全部解决,则将
P 推回堆栈。然后将P 的任何尚未解决的子问题也推入堆栈。
随着您的进展,您的堆栈最初会随着您将主要问题及其子问题以及子问题推入堆栈而增长。然后您将开始解决较小的实例并将结果放入缓存中,直到最终您拥有解决主要问题所需的一切。
与递归自上而下的方法相比,它使用的内存并没有显着减少,但它确实使用了堆空间而不是 JVM 堆栈空间,这意味着它可以更好地扩展,因为 JVM 堆栈比堆小得多。
不过,这相当困难。至少,在开始编写更困难的版本之前,请保留您的工作解决方案!