今天要討論的是集合覆蓋問題~概念其實跟昨天的旅行售貨員問題很相似喔!
有 N 個元素以及 M 個子集合,請你找出最少的子集合,使得每一個元素都出現在至少一個被挑選的子集合內。
直接上題吧!
https://leetcode.com/problems/smallest-sufficient-team/
現在給你一些必備技能列表,以及每個人到底會哪些技能。請你找出人數最少的隊伍,使得每一項列表上的技能該隊伍裡面都有人會。
考慮任何技能列表的一個子集合 S,我們可以令 dp(S) 表示湊滿集合 S 的時候最少需要多少人(然後順便留下一個 list 表示一組解)。顯然 dp(空集合) = []。當我們找出 dp(S) 的時候,可以掃過所有人,試圖把這個人加進隊伍中,看看新的 dp(S+這個人會的東西) 當前的 list 會不會比較短,如果比較短的話就更新上去!這是一個主動式更新很好寫的例子!
class Solution:
def smallestSufficientTeam(self, req_skills: List[str], people: List[List[str]]) -> List[int]:
def get_mask(skill_list):
return sum([2**req_skills.index(skill) for skill in skill_list])
masks = [get_mask(skill_list) for skill_list in people]
dp = {}
dp[0] = []
for state in range(0, 2**len(req_skills)):
if dp.get(state) == None:
continue
for i, mask in enumerate(masks):
if dp.get(state|mask) == None or len(dp[state|mask]) > len(dp[state]) + 1:
dp[state|mask] = dp[state] + [i]
return dp[2**len(req_skills)-1]
我們可以用費氏數列的例子來講解 Push 和 Pull 的差別:
前幾天我們提到了良好定義動態規劃的狀態,可以得到很棒的執行效能。接下來我們要開始聊聊,在定義好動態規劃以後,如果有良好性質的話(比方說各種單調性),就可以用理論方式跳過許多不可能是最佳解的子問題,進而達到各種動態規劃的加速了~
嗯...先從 LIS 開始好了。