Skip to content

Commit

Permalink
go
Browse files Browse the repository at this point in the history
  • Loading branch information
robot committed Jun 12, 2022
1 parent 89cd865 commit 3b97fcb
Showing 1 changed file with 13 additions and 4 deletions.
17 changes: 13 additions & 4 deletions problems/1494.parallel-courses-ii.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ xi != yi

> 一般 20 以内都可以,具体的时间复杂度和数据规模的关系可从[我的网站](https://leetcode-pp.github.io/leetcode-cheat/)中的复杂度速查表中查看。
再比如 [5289. 公平分发饼干](https://leetcode.cn/problems/fair-distribution-of-cookies/) 看下数据范围就大概知道很可能就是回溯或者状压 DP。

这道题是状压 DP,如果你对其不了解,可以看下我之前写的 [状压 DP 是什么?这篇题解带你入门](https://mp.weixin.qq.com/s/ecxTTrRvUJbdWwSFbKgDiw),这里一些细节不再赘述。

首先,我们需要用一个数据结构来存储课程之间的依赖关系。不妨使用 hashmap,这样可以在 $O(1)$ 的时间获取到一个课程的所有前置课程。
Expand Down Expand Up @@ -134,11 +136,17 @@ for (i = x; i != 0; i = (i - 1) & x) {

有了上面的铺垫就简单了。我们只需要枚举所有子集,对于每一个子集,我们考虑使用动态规划来转移状态。

定义 dp[studied] 为学习情况为 studied 的最少需要学期数。

那么转移方程为:

```py
dp[i | sub] = min(dp[i | sub], dp[i] + 1)
# 含义为我们可以选择在这一学期学习 sub,或者选择在下一学期学习 sub
# dp[studied | sub] 就是两种选择的较小值
dp[studied | sub] = min(dp[studied | sub], dp[studied] + 1)
```

其中 i 为当前的学习的课程,sub 为当前可以学习的课程的子集。其中 i 和 sub 都是一个数字,每一位的 bit 为 0 表示无该课程,为 1 表示有该课程。
其中 studied 为当前的学习的课程,sub 为当前可以学习的课程的子集。其中 studied 和 sub 都是一个数字,每一位的 bit 为 0 表示无该课程,为 1 表示有该课程。

## 关键点

Expand All @@ -160,7 +168,7 @@ class Solution:

for fr, to in dependencies:
neighbors[to - 1] |= 1 << (fr - 1)
dp[0] = 0 # 启动 dp
dp[0] = 0 # 表示什么都不学的情况需要 0 学期
for i in range(1 << n):
can = 0
for j in range(n):
Expand All @@ -170,9 +178,10 @@ class Solution:
can &= ~i
sub = can
while sub:
# 可以学习 sub
if bin(sub).count("1") <= k:
dp[i | sub] = min(dp[i | sub], dp[i] + 1)
sub = (sub - 1) & can
sub = (sub - 1) & can # 快速跳到下一个子集(枚举子集优化)
return dp[(1 << n) - 1]


Expand Down

0 comments on commit 3b97fcb

Please sign in to comment.