@@ -63,6 +63,8 @@ xi != yi
6363
6464> 一般 20 以内都可以,具体的时间复杂度和数据规模的关系可从[ 我的网站] ( https://leetcode-pp.github.io/leetcode-cheat/ ) 中的复杂度速查表中查看。
6565
66+ 再比如 [ 5289. 公平分发饼干] ( https://leetcode.cn/problems/fair-distribution-of-cookies/ ) 看下数据范围就大概知道很可能就是回溯或者状压 DP。
67+
6668这道题是状压 DP,如果你对其不了解,可以看下我之前写的 [ 状压 DP 是什么?这篇题解带你入门] ( https://mp.weixin.qq.com/s/ecxTTrRvUJbdWwSFbKgDiw ) ,这里一些细节不再赘述。
6769
6870首先,我们需要用一个数据结构来存储课程之间的依赖关系。不妨使用 hashmap,这样可以在 $O(1)$ 的时间获取到一个课程的所有前置课程。
@@ -134,11 +136,17 @@ for (i = x; i != 0; i = (i - 1) & x) {
134136
135137有了上面的铺垫就简单了。我们只需要枚举所有子集,对于每一个子集,我们考虑使用动态规划来转移状态。
136138
139+ 定义 dp[ studied] 为学习情况为 studied 的最少需要学期数。
140+
141+ 那么转移方程为:
142+
137143``` py
138- dp[i | sub] = min (dp[i | sub], dp[i] + 1 )
144+ # 含义为我们可以选择在这一学期学习 sub,或者选择在下一学期学习 sub
145+ # dp[studied | sub] 就是两种选择的较小值
146+ dp[studied | sub] = min (dp[studied | sub], dp[studied] + 1 )
139147```
140148
141- 其中 i 为当前的学习的课程,sub 为当前可以学习的课程的子集。其中 i 和 sub 都是一个数字,每一位的 bit 为 0 表示无该课程,为 1 表示有该课程。
149+ 其中 studied 为当前的学习的课程,sub 为当前可以学习的课程的子集。其中 studied 和 sub 都是一个数字,每一位的 bit 为 0 表示无该课程,为 1 表示有该课程。
142150
143151## 关键点
144152
@@ -160,7 +168,7 @@ class Solution:
160168
161169 for fr, to in dependencies:
162170 neighbors[to - 1 ] |= 1 << (fr - 1 )
163- dp[0 ] = 0 # 启动 dp
171+ dp[0 ] = 0 # 表示什么都不学的情况需要 0 学期
164172 for i in range (1 << n):
165173 can = 0
166174 for j in range (n):
@@ -170,9 +178,10 @@ class Solution:
170178 can &= ~ i
171179 sub = can
172180 while sub:
181+ # 可以学习 sub
173182 if bin (sub).count(" 1" ) <= k:
174183 dp[i | sub] = min (dp[i | sub], dp[i] + 1 )
175- sub = (sub - 1 ) & can
184+ sub = (sub - 1 ) & can # 快速跳到下一个子集(枚举子集优化)
176185 return dp[(1 << n) - 1 ]
177186
178187
0 commit comments