leetcode
leetcode 1651 ~ 1700
恰有 K 根木棍可以看到的排列数目

恰有 K 根木棍可以看到的排列数目

难度:

标签:

题目描述

代码结果

运行时间: 207 ms, 内存: 69.5 MB


// Java Stream 不太适合处理此类需要二维数组和动态规划的复杂问题。
// 因此,在这里不使用 Stream API,而是使用普通的二维数组和循环解决问题。
// 代码实现和逻辑与上面普通 Java 版本相同。

解释

方法:

此题解采用了递归+记忆化的方法来解决问题。首先,定义一个递归函数 `getRes(n, k)` 来计算有n根木棍时,恰好有k根木棍可见的排列数。对于基本情况,如果要求的可见木棍数k等于总棍数n,则只有一种排列满足要求。如果k为1,则任何排列中最长的木棍都必须放在最前面,此后的n-1根木棍可以任意排列,因此是(n-1)!种排列。对于一般情况,将问题分为两部分:1) 第n根木棍不增加可见木棍数的情况,此时它必须被前面的某根木棍遮挡,因此这种情况下的排列数为 `getRes(n-1, k) * (n-1)`;2) 第n根木棍增加可见木棍数的情况,即它是可见的,这种情况下的排列数为 `getRes(n-1, k-1)`。最终结果是这两种情况之和,模10^9+7。

时间复杂度:

O(n * k)

空间复杂度:

O(n * k)

代码细节讲解

🦆
递归函数`getRes(n, k)`中,对于`n < k`的情况应该如何处理?题解中未提及这种情况。
当`n < k`时,不可能有超过n根木棍可见,因为可见的木棍数不可能超过木棍的总数。因此,在这种情况下,应返回0,表示不存在这样的排列。这是一个边界情况,应在函数`getRes`中明确处理以避免错误的递归调用。
🦆
在`getRes(n, k)`函数的递归调用中,为什么将`getRes(n-1, k) * (n-1)`视为一种情况?这里的`(n-1)`乘法因子具体代表什么?
在`getRes(n-1, k) * (n-1)`这种情况下,考虑第n根木棍不是新的可见木棍(即它被之前的某一根木棍遮挡)。`getRes(n-1, k)`计算了n-1根木棍中有k根可见的排列数。乘以`(n-1)`的原因是,第n根木棍可以插入到前n-1根木棍的任何一个位置(除了最前面),而不改变原有的可见木棍数量,共有n-1种选择。
🦆
题解中提到的记忆化方法具体是如何实现的?能否详细解释`lru_cache`的作用和它如何影响递归函数的性能?
`lru_cache`是Python标准库`functools`中的一个装饰器,用于实现记忆化功能。它缓存装饰函数的调用结果,当函数用相同的参数再次被调用时,直接从缓存中返回结果,而不需要重新执行函数。这极大地提高了递归函数的性能,特别是在处理有大量重复子问题的递归调用时,如本题中的`getRes`和`factorial`。通过避免重复计算,减少了时间复杂度,使得算法能够在可接受的时间内解决问题。
🦆
题解中的阶乘函数`factorial(k)`在k为1时直接返回1,但未处理k为0的情况,这是否可能导致问题?
在数学中,0的阶乘定义为1 (`0! = 1`)。题解中的`factorial`函数没有显式处理k为0的情况,这可能导致在特定情况下调用`factorial(0)`时出现错误。为了完善函数并避免潜在问题,应该在函数开始添加一个条件,检查k是否为0,如果是,则返回1。这样可以确保函数在所有情况下都正确运行。

相关问题