返回

**LeetCode 1418. 点歌时间 总持续时间可被 60 整除的歌曲**

前端

引言

音乐播放列表中有一系列歌曲,这些歌曲的持续时间以秒为单位给出。现在,需要从播放列表中选择一些歌曲,使得它们的总持续时间恰好可以被 60 整除。换句话说,就是要求所选歌曲的总持续时间是 60 的倍数。

解题思路

1. 模拟

我们可以使用模拟法逐一尝试从播放列表中选择歌曲。对于每首歌,我们都可以将其持续时间添加到当前已选择的歌曲的总持续时间中。如果添加后总持续时间可以被 60 整除,那么我们就找到了一个可行解。

2. 公式

更优化的解法是利用数学公式。假设播放列表中有 n 首歌曲,它们的持续时间分别为 t1、t2、...、tn。那么,要使总持续时间恰好可以被 60 整除,等价于:

t1 + t2 + ... + tn ≡ 0 (mod 60)

其中,≡ 表示同余运算。

基于这个公式,我们可以使用动态规划的方法解决这个问题。具体步骤如下:

步骤 1:初始化

创建一个二维数组 dp,其中 dp[i][j] 表示考虑前 i 首歌曲,并且剩余时间为 j 时,能否找到一个可行解。

步骤 2:状态转移

对于第 i 首歌曲,有两种选择:

  1. 不选择第 i 首歌曲:dp[i][j] = dp[i - 1][j]
  2. 选择第 i 首歌曲:dp[i][j] = dp[i - 1][j - t[i]]

步骤 3:边界条件

当 j 为 0 时,表示已经找到了一个可行解,因此 dp[i][0] = true。

步骤 4:结果

最后,检查 dp[n][0] 是否为 true。如果是,则存在可行解;否则,不存在可行解。

代码实现

JavaScript

const canChooseSongs = (time) => {
  // 剩余时间范围
  const MOD = 60;
  const n = time.length;
  // 初始化 dp 数组
  const dp = Array(n + 1).fill(null).map(() => Array(MOD + 1).fill(false));
  dp[0][0] = true;
  // 状态转移
  for (let i = 1; i <= n; i++) {
    for (let j = 0; j <= MOD; j++) {
      dp[i][j] = dp[i - 1][j];
      if (j >= time[i - 1]) {
        dp[i][j] |= dp[i - 1][j - time[i - 1]];
      }
    }
  }
  return dp[n][0];
};

时间复杂度 :O(n * MOD)

空间复杂度 :O(n * MOD)

总结

通过模拟和公式相结合的方法,我们能够高效地解决 LeetCode 1418 题「点歌时间」的问题。该解法不仅清晰易懂,而且可以很好地扩展到类似的问题中。