返回

算法优化:会议最大化 - 「朴素DP」与「二分优化 DP」

后端







## 前言

在日常生活中,安排会议是一个常见的任务。如何高效安排会议,合理利用时间,成为我们提高工作效率的重要课题之一。算法优化可以帮助我们从「数学建模」的角度解决问题,从而得出最优解。

## 问题

在 LeetCode 的「1751. 最多可以参加的会议数目 II」中,我们需要解决这样一个问题:

给你一个会议时间安排的数组 intervals,其中 intervals[i] = [starti, endi] 表示第 i 个会议的起始时间 starti 及结束时间 endi,会议不会重叠。

现有一个会议室可以用来预定会议,同一个时间段只能预定一个会议。若会议安排的总时间为 t,返回在这个时间段内可以预定的最多会议数目。

## 朴素 DP

朴素 DP 是解决动态规划问题的常见方法。对于这个问题,我们定义状态 dp[i][j] 表示前 i 个会议在总时间为 j 时可以参加的最大会议数目。状态转移方程为:

dp[i][j] = max(dp[i-1][j], dp[i-1][j - time[i]] + 1)


其中,time[i] 表示第 i 个会议的时间。

## 二分优化 DP

朴素 DP 的时间复杂度为 O(n * t),其中 n 是会议数目,t 是总时间。为了提高算法效率,我们可以使用二分优化 DP 来缩减时间复杂度。

二分优化 DP 的关键思想在于,我们可以通过二分查找找到一个时间阈值,在这个阈值以下的会议都可以被安排,而在这个阈值以上的会议则无法安排。这样,我们可以将问题分解为两个子问题:

1. 求出时间阈值以下的最多会议数目。
2. 求出时间阈值以上的最多会议数目。

通过递归的方式,我们可以最终求出总时间为 t 时最多可以安排的会议数目。

## 算法实现

### 朴素 DP

```python
def max_meetings(intervals, t):
  """
  朴素 DP 求解「1751. 最多可以参加的会议数目 II」问题。

  Args:
    intervals: 会议时间安排的数组,其中 intervals[i] = [starti, endi] 表示第 i 个会议的起始时间 starti 及结束时间 endi。
    t: 总时间。

  Returns:
    这个时间段内可以预定的最多会议数目。
  """

  # 初始化动态规划表。
  dp = [[0] * (t + 1) for _ in range(len(intervals) + 1)]

  # 填充动态规划表。
  for i in range(1, len(intervals) + 1):
    for j in range(1, t + 1):
      dp[i][j] = max(dp[i-1][j], dp[i-1][j - intervals[i-1][1]] + 1)

  # 返回结果。
  return dp[len(intervals)][t]

二分优化 DP

def max_meetings_binary_search(intervals, t):
  """
  二分优化 DP 求解「1751. 最多可以参加的会议数目 II」问题。

  Args:
    intervals: 会议时间安排的数组,其中 intervals[i] = [starti, endi] 表示第 i 个会议的起始时间 starti 及结束时间 endi。
    t: 总时间。

  Returns:
    这个时间段内可以预定的最多会议数目。
  """

  # 计算所有会议的总时间。
  total_time = sum(interval[1] - interval[0] for interval in intervals)

  # 如果总时间小于等于 t,则可以安排所有会议。
  if total_time <= t:
    return len(intervals)

  # 二分查找时间阈值。
  left, right = 0, t
  while left < right:
    mid = (left + right) // 2

    # 计算时间阈值以下的最多会议数目。
    max_meetings_below_threshold = 0
    for interval in intervals:
      if interval[1] <= mid:
        max_meetings_below_threshold += 1

    # 如果时间阈值以下的最多会议数目大于总时间,则将时间阈值减半。
    if max_meetings_below_threshold > total_time - mid:
      right = mid
    # 否则,将时间阈值加半。
    else:
      left = mid + 1

  # 返回时间阈值以下的最多会议数目。
  return max_meetings_below_threshold

算法比较

算法 时间复杂度 空间复杂度
朴素 DP O(n * t) O(n * t)
二分优化 DP O(n * log(t)) O(n)

总结

本文通过朴素 DP 与二分优化 DP 两种方法,解决了 LeetCode 上的「1751. 最多可以参加的会议数目 II」难题。其中,朴素 DP 的算法实现较为直观,但时间复杂度较高。而二分优化 DP 的算法实现更为复杂,但时间复杂度更低。

在实际应用中,我们应该根据问题的具体情况选择合适的算法。如果问题规模较小,则可以使用朴素 DP 来求解;如果问题规模较大,则可以使用二分优化 DP 来求解。